pc, pc64, xen: rewrite interrupt handling code
This implements proper intrdisable() support for all interrupt controllers. For enable, (*arch->intrassign)(Vctl*) fills in the Vctl.enable and Vctl.disable pointers with the appropriate routines and returns the assigned vector number. Once the Vctl struct has been linked to its vector chain, Vctl.enable(Vctl*, shared) gets called with a flag if the vector has been already enabled (shared). This order is important here as enabling the interrupt on the controller before we have linked the chain can cause spurious interrupts, expecially on mp system where the interrupt can target a different cpu than the caller of intrenable(). The intrdisable() case is the other way around. We first disable the interrupt on the controller and after that unlink the Vctl from the chain. On a multiprocessor, the xfree() of the Vctl struct is delayed to avoid freeing it while it is still in use by another cpu. The xen port now also uses pc/irq.c which has been made generic enougth to handle xen's irq scheme. Also, archgeneric is now a separate file to avoid pulling in dependencies from the 8259 interrupt controller code.
This commit is contained in:
parent
32a5ff9658
commit
1d93a5628a
23 changed files with 488 additions and 664 deletions
|
@ -256,6 +256,7 @@ Foundbus:
|
||||||
ai->intr = pi;
|
ai->intr = pi;
|
||||||
ai->apic = a;
|
ai->apic = a;
|
||||||
ai->next = bus->aintr;
|
ai->next = bus->aintr;
|
||||||
|
ai->bus = bus;
|
||||||
bus->aintr = ai;
|
bus->aintr = ai;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,13 +734,15 @@ acpireset(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int identify(void);
|
static int identify(void);
|
||||||
|
extern int i8259irqno(int, int);
|
||||||
|
|
||||||
PCArch archacpi = {
|
PCArch archacpi = {
|
||||||
.id= "ACPI",
|
.id= "ACPI",
|
||||||
.ident= identify,
|
.ident= identify,
|
||||||
.reset= acpireset,
|
.reset= acpireset,
|
||||||
.intrinit= acpiinit,
|
.intrinit= acpiinit,
|
||||||
.intrenable= mpintrenable,
|
.intrassign= mpintrassign,
|
||||||
|
.intrirqno= i8259irqno,
|
||||||
.intron= lapicintron,
|
.intron= lapicintron,
|
||||||
.introff= lapicintroff,
|
.introff= lapicintroff,
|
||||||
.fastclock= i8253read,
|
.fastclock= i8253read,
|
||||||
|
|
59
sys/src/9/pc/archgeneric.c
Normal file
59
sys/src/9/pc/archgeneric.c
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#include "u.h"
|
||||||
|
#include "../port/lib.h"
|
||||||
|
#include "mem.h"
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "ureg.h"
|
||||||
|
#include "../port/error.h"
|
||||||
|
|
||||||
|
extern int i8259assign(Vctl*);
|
||||||
|
extern int i8259irqno(int, int);
|
||||||
|
extern void i8259init(void);
|
||||||
|
extern int i8259isr(int);
|
||||||
|
extern void i8259on(void);
|
||||||
|
extern void i8259off(void);
|
||||||
|
extern int i8259vecno(int);
|
||||||
|
|
||||||
|
void
|
||||||
|
archreset(void)
|
||||||
|
{
|
||||||
|
i8042reset();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Often the BIOS hangs during restart if a conventional 8042
|
||||||
|
* warm-boot sequence is tried. The following is Intel specific and
|
||||||
|
* seems to perform a cold-boot, but at least it comes back.
|
||||||
|
* And sometimes there is no keyboard...
|
||||||
|
*
|
||||||
|
* The reset register (0xcf9) is usually in one of the bridge
|
||||||
|
* chips. The actual location and sequence could be extracted from
|
||||||
|
* ACPI but why bother, this is the end of the line anyway.
|
||||||
|
*/
|
||||||
|
print("Takes a licking and keeps on ticking...\n");
|
||||||
|
*(ushort*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */
|
||||||
|
outb(0xcf9, 0x02);
|
||||||
|
outb(0xcf9, 0x06);
|
||||||
|
|
||||||
|
print("can't reset\n");
|
||||||
|
for(;;)
|
||||||
|
idle();
|
||||||
|
}
|
||||||
|
|
||||||
|
PCArch archgeneric = {
|
||||||
|
.id= "generic",
|
||||||
|
.ident= 0,
|
||||||
|
.reset= archreset,
|
||||||
|
|
||||||
|
.intrinit= i8259init,
|
||||||
|
.intrassign= i8259assign,
|
||||||
|
.intrirqno= i8259irqno,
|
||||||
|
.intrvecno= i8259vecno,
|
||||||
|
.intrspurious= i8259isr,
|
||||||
|
.intron= i8259on,
|
||||||
|
.introff= i8259off,
|
||||||
|
|
||||||
|
.clockenable= i8253enable,
|
||||||
|
.fastclock= i8253read,
|
||||||
|
.timerset= i8253timerset,
|
||||||
|
};
|
|
@ -194,6 +194,7 @@ mkiointr(PCMPintr* p)
|
||||||
}
|
}
|
||||||
aintr->apic = mpioapic[p->apicno];
|
aintr->apic = mpioapic[p->apicno];
|
||||||
aintr->next = bus->aintr;
|
aintr->next = bus->aintr;
|
||||||
|
aintr->bus = bus;
|
||||||
bus->aintr = aintr;
|
bus->aintr = aintr;
|
||||||
|
|
||||||
return aintr;
|
return aintr;
|
||||||
|
@ -366,13 +367,15 @@ mpreset(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int identify(void);
|
static int identify(void);
|
||||||
|
extern int i8259irqno(int, int);
|
||||||
|
|
||||||
PCArch archmp = {
|
PCArch archmp = {
|
||||||
.id= "_MP_",
|
.id= "_MP_",
|
||||||
.ident= identify,
|
.ident= identify,
|
||||||
.reset= mpreset,
|
.reset= mpreset,
|
||||||
.intrinit= pcmpinit,
|
.intrinit= pcmpinit,
|
||||||
.intrenable= mpintrenable,
|
.intrassign= mpintrassign,
|
||||||
|
.intrirqno= i8259irqno,
|
||||||
.intron= lapicintron,
|
.intron= lapicintron,
|
||||||
.introff= lapicintroff,
|
.introff= lapicintroff,
|
||||||
.fastclock= i8253read,
|
.fastclock= i8253read,
|
||||||
|
|
|
@ -280,13 +280,12 @@ struct PCArch
|
||||||
char* id;
|
char* id;
|
||||||
int (*ident)(void); /* this should be in the model */
|
int (*ident)(void); /* this should be in the model */
|
||||||
void (*reset)(void); /* this should be in the model */
|
void (*reset)(void); /* this should be in the model */
|
||||||
int (*serialpower)(int); /* 1 == on, 0 == off */
|
|
||||||
int (*modempower)(int); /* 1 == on, 0 == off */
|
|
||||||
|
|
||||||
void (*intrinit)(void);
|
void (*intrinit)(void);
|
||||||
int (*intrenable)(Vctl*);
|
int (*intrassign)(Vctl*);
|
||||||
|
int (*intrirqno)(int, int);
|
||||||
|
int (*intrspurious)(int);
|
||||||
int (*intrvecno)(int);
|
int (*intrvecno)(int);
|
||||||
int (*intrdisable)(int);
|
|
||||||
void (*introff)(void);
|
void (*introff)(void);
|
||||||
void (*intron)(void);
|
void (*intron)(void);
|
||||||
|
|
||||||
|
|
|
@ -324,31 +324,6 @@ nop(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
archreset(void)
|
|
||||||
{
|
|
||||||
i8042reset();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Often the BIOS hangs during restart if a conventional 8042
|
|
||||||
* warm-boot sequence is tried. The following is Intel specific and
|
|
||||||
* seems to perform a cold-boot, but at least it comes back.
|
|
||||||
* And sometimes there is no keyboard...
|
|
||||||
*
|
|
||||||
* The reset register (0xcf9) is usually in one of the bridge
|
|
||||||
* chips. The actual location and sequence could be extracted from
|
|
||||||
* ACPI but why bother, this is the end of the line anyway.
|
|
||||||
*/
|
|
||||||
print("Takes a licking and keeps on ticking...\n");
|
|
||||||
*(ushort*)KADDR(0x472) = 0x1234; /* BIOS warm-boot flag */
|
|
||||||
outb(0xcf9, 0x02);
|
|
||||||
outb(0xcf9, 0x06);
|
|
||||||
|
|
||||||
print("can't reset\n");
|
|
||||||
for(;;)
|
|
||||||
idle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 386 has no compare-and-swap instruction.
|
* 386 has no compare-and-swap instruction.
|
||||||
* Run it with interrupts turned off instead.
|
* Run it with interrupts turned off instead.
|
||||||
|
@ -380,25 +355,6 @@ int (*cmpswap)(long*, long, long) = cmpswap386;
|
||||||
PCArch* arch;
|
PCArch* arch;
|
||||||
extern PCArch* knownarch[];
|
extern PCArch* knownarch[];
|
||||||
|
|
||||||
PCArch archgeneric = {
|
|
||||||
.id= "generic",
|
|
||||||
.ident= 0,
|
|
||||||
.reset= archreset,
|
|
||||||
.serialpower= unimplemented,
|
|
||||||
.modempower= unimplemented,
|
|
||||||
|
|
||||||
.intrinit= i8259init,
|
|
||||||
.intrenable= i8259enable,
|
|
||||||
.intrvecno= i8259vecno,
|
|
||||||
.intrdisable= i8259disable,
|
|
||||||
.intron= i8259on,
|
|
||||||
.introff= i8259off,
|
|
||||||
|
|
||||||
.clockenable= i8253enable,
|
|
||||||
.fastclock= i8253read,
|
|
||||||
.timerset= i8253timerset,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct X86type X86type;
|
typedef struct X86type X86type;
|
||||||
struct X86type {
|
struct X86type {
|
||||||
int family;
|
int family;
|
||||||
|
@ -912,26 +868,22 @@ archinit(void)
|
||||||
{
|
{
|
||||||
PCArch **p;
|
PCArch **p;
|
||||||
|
|
||||||
arch = &archgeneric;
|
arch = knownarch[0];
|
||||||
for(p = knownarch; *p != nil; p++){
|
for(p = knownarch; *p != nil; p++){
|
||||||
if((*p)->ident != nil && (*p)->ident() == 0){
|
if((*p)->ident != nil && (*p)->ident() == 0){
|
||||||
arch = *p;
|
arch = *p;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(arch != &archgeneric){
|
if(arch != knownarch[0]){
|
||||||
if(arch->id == nil)
|
if(arch->id == nil)
|
||||||
arch->id = archgeneric.id;
|
arch->id = knownarch[0]->id;
|
||||||
if(arch->reset == nil)
|
if(arch->reset == nil)
|
||||||
arch->reset = archgeneric.reset;
|
arch->reset = knownarch[0]->reset;
|
||||||
if(arch->serialpower == nil)
|
|
||||||
arch->serialpower = archgeneric.serialpower;
|
|
||||||
if(arch->modempower == nil)
|
|
||||||
arch->modempower = archgeneric.modempower;
|
|
||||||
if(arch->intrinit == nil)
|
if(arch->intrinit == nil)
|
||||||
arch->intrinit = archgeneric.intrinit;
|
arch->intrinit = knownarch[0]->intrinit;
|
||||||
if(arch->intrenable == nil)
|
if(arch->intrassign == nil)
|
||||||
arch->intrenable = archgeneric.intrenable;
|
arch->intrassign = knownarch[0]->intrassign;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1100,6 +1052,33 @@ dumpmcregs(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nmihandler(Ureg *ureg, void*)
|
||||||
|
{
|
||||||
|
iprint("cpu%d: nmi PC %#p, status %ux\n",
|
||||||
|
m->machno, ureg->pc, inb(0x61));
|
||||||
|
while(m->machno != 0)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nmienable(void)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
|
||||||
|
trapenable(VectorNMI, nmihandler, nil, "nmi");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hack: should be locked with NVRAM access.
|
||||||
|
*/
|
||||||
|
outb(0x70, 0x80); /* NMI latch clear */
|
||||||
|
outb(0x70, 0);
|
||||||
|
|
||||||
|
x = inb(0x61) & 0x07; /* Enable NMI */
|
||||||
|
outb(0x61, 0x0C|x);
|
||||||
|
outb(0x61, x);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
setupwatchpts(Proc *pr, Watchpt *wp, int nwp)
|
setupwatchpts(Proc *pr, Watchpt *wp, int nwp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,13 +65,6 @@ void i8253init(void);
|
||||||
void i8253reset(void);
|
void i8253reset(void);
|
||||||
uvlong i8253read(uvlong*);
|
uvlong i8253read(uvlong*);
|
||||||
void i8253timerset(uvlong);
|
void i8253timerset(uvlong);
|
||||||
int i8259disable(int);
|
|
||||||
int i8259enable(Vctl*);
|
|
||||||
void i8259init(void);
|
|
||||||
int i8259isr(int);
|
|
||||||
void i8259on(void);
|
|
||||||
void i8259off(void);
|
|
||||||
int i8259vecno(int);
|
|
||||||
void idle(void);
|
void idle(void);
|
||||||
void idlehands(void);
|
void idlehands(void);
|
||||||
int inb(int);
|
int inb(int);
|
||||||
|
@ -111,6 +104,7 @@ char* mtrr(uvlong, uvlong, char *);
|
||||||
void mtrrclock(void);
|
void mtrrclock(void);
|
||||||
int mtrrprint(char *, long);
|
int mtrrprint(char *, long);
|
||||||
void mtrrsync(void);
|
void mtrrsync(void);
|
||||||
|
void nmienable(void);
|
||||||
uchar nvramread(int);
|
uchar nvramread(int);
|
||||||
void nvramwrite(int, uchar);
|
void nvramwrite(int, uchar);
|
||||||
void outb(int, int);
|
void outb(int, int);
|
||||||
|
|
|
@ -127,8 +127,38 @@ i8259isr(int vno)
|
||||||
return isr & (1<<irq);
|
return isr & (1<<irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
irqenable(Vctl *v, int shared)
|
||||||
|
{
|
||||||
|
if(shared)
|
||||||
|
return 0;
|
||||||
|
ilock(&i8259lock);
|
||||||
|
i8259mask &= ~(1<<v->irq);
|
||||||
|
if(v->irq < 8)
|
||||||
|
outb(Int0aux, i8259mask & 0xFF);
|
||||||
|
else
|
||||||
|
outb(Int1aux, (i8259mask>>8) & 0xFF);
|
||||||
|
iunlock(&i8259lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
irqdisable(Vctl *v, int shared)
|
||||||
|
{
|
||||||
|
if(shared)
|
||||||
|
return 0;
|
||||||
|
ilock(&i8259lock);
|
||||||
|
i8259mask |= 1<<v->irq;
|
||||||
|
if(v->irq < 8)
|
||||||
|
outb(Int0aux, i8259mask & 0xFF);
|
||||||
|
else
|
||||||
|
outb(Int1aux, (i8259mask>>8) & 0xFF);
|
||||||
|
iunlock(&i8259lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
i8259enable(Vctl* v)
|
i8259assign(Vctl *v)
|
||||||
{
|
{
|
||||||
int irq, irqbit;
|
int irq, irqbit;
|
||||||
|
|
||||||
|
@ -150,54 +180,42 @@ i8259enable(Vctl* v)
|
||||||
iunlock(&i8259lock);
|
iunlock(&i8259lock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
i8259mask &= ~irqbit;
|
iunlock(&i8259lock);
|
||||||
if(irq < 8)
|
|
||||||
outb(Int0aux, i8259mask & 0xFF);
|
|
||||||
else
|
|
||||||
outb(Int1aux, (i8259mask>>8) & 0xFF);
|
|
||||||
|
|
||||||
if(i8259elcr & irqbit)
|
if(i8259elcr & irqbit)
|
||||||
v->eoi = i8259isr;
|
v->eoi = i8259isr;
|
||||||
else
|
else
|
||||||
v->isr = i8259isr;
|
v->isr = i8259isr;
|
||||||
iunlock(&i8259lock);
|
|
||||||
|
v->enable = irqenable;
|
||||||
|
v->disable = irqdisable;
|
||||||
|
|
||||||
return VectorPIC+irq;
|
return VectorPIC+irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
i8259irqno(int irq, int tbdf)
|
||||||
|
{
|
||||||
|
if(tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ2 doesn't really exist, it's used to gang the interrupt
|
||||||
|
* controllers together. A device set to IRQ2 will appear on
|
||||||
|
* the second interrupt controller as IRQ9.
|
||||||
|
*/
|
||||||
|
if(irq == 2)
|
||||||
|
irq = 9;
|
||||||
|
|
||||||
|
return irq;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
i8259vecno(int irq)
|
i8259vecno(int irq)
|
||||||
{
|
{
|
||||||
return VectorPIC+irq;
|
return VectorPIC+irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
i8259disable(int irq)
|
|
||||||
{
|
|
||||||
int irqbit;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Given an IRQ, disable the corresponding interrupt
|
|
||||||
* in the 8259.
|
|
||||||
*/
|
|
||||||
if(irq < 0 || irq > MaxIrqPIC){
|
|
||||||
print("i8259disable: irq %d out of range\n", irq);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
irqbit = 1<<irq;
|
|
||||||
|
|
||||||
ilock(&i8259lock);
|
|
||||||
if(!(i8259mask & irqbit)){
|
|
||||||
i8259mask |= irqbit;
|
|
||||||
if(irq < 8)
|
|
||||||
outb(Int0aux, i8259mask & 0xFF);
|
|
||||||
else
|
|
||||||
outb(Int1aux, (i8259mask>>8) & 0xFF);
|
|
||||||
}
|
|
||||||
iunlock(&i8259lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
i8259on(void)
|
i8259on(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,19 +46,23 @@ enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct Vctl {
|
typedef struct Vctl {
|
||||||
Vctl* next; /* handlers on this vector */
|
Vctl *next; /* handlers on this vector */
|
||||||
|
|
||||||
void (*f)(Ureg*, void*); /* handler to call */
|
void (*f)(Ureg*, void*); /* handler to call */
|
||||||
void* a; /* argument to call it with */
|
void *a; /* argument to call it with */
|
||||||
|
|
||||||
int isintr; /* interrupt or fault/trap */
|
|
||||||
|
|
||||||
int (*isr)(int); /* get isr bit for this irq */
|
int (*isr)(int); /* get isr bit for this irq */
|
||||||
int (*eoi)(int); /* eoi */
|
int (*eoi)(int); /* eoi */
|
||||||
|
|
||||||
void (*disable)(Vctl*);
|
int (*enable)(Vctl*, int);
|
||||||
|
int (*disable)(Vctl*, int);
|
||||||
|
void *aux;
|
||||||
|
|
||||||
int irq;
|
int irq;
|
||||||
int tbdf;
|
int tbdf;
|
||||||
|
int vno;
|
||||||
|
int cpu;
|
||||||
|
int local;
|
||||||
|
|
||||||
char name[KNAMELEN]; /* of driver */
|
char name[KNAMELEN]; /* of driver */
|
||||||
} Vctl;
|
} Vctl;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "../port/error.h"
|
#include "../port/error.h"
|
||||||
|
|
||||||
static Lock vctllock;
|
static Lock vctllock;
|
||||||
static Vctl *vctl[256];
|
static Vctl *vclock, *vctl[256];
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -46,27 +46,26 @@ irqhandled(Ureg *ureg, int vno)
|
||||||
Vctl *ctl, *v;
|
Vctl *ctl, *v;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if(ctl = vctl[vno]){
|
ctl = vctl[vno];
|
||||||
if(ctl->isintr){
|
if(ctl != nil){
|
||||||
|
if(vno < VectorPIC){
|
||||||
|
(*ctl->f)(ureg, ctl->a);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
m->perf.intrts = perfticks();
|
m->perf.intrts = perfticks();
|
||||||
m->intr++;
|
m->intr++;
|
||||||
if(vno >= VectorPIC)
|
|
||||||
m->lastintr = ctl->irq;
|
m->lastintr = ctl->irq;
|
||||||
}
|
if(ctl->isr != nil)
|
||||||
if(ctl->isr)
|
(*ctl->isr)(vno);
|
||||||
ctl->isr(vno);
|
for(v = ctl; v != nil; v = v->next)
|
||||||
for(v = ctl; v != nil; v = v->next){
|
(*v->f)(ureg, v->a);
|
||||||
if(v->f)
|
if(ctl->eoi != nil)
|
||||||
v->f(ureg, v->a);
|
(*ctl->eoi)(vno);
|
||||||
}
|
|
||||||
if(ctl->eoi)
|
|
||||||
ctl->eoi(vno);
|
|
||||||
|
|
||||||
if(ctl->isintr){
|
|
||||||
intrtime(m, vno);
|
intrtime(m, vno);
|
||||||
|
|
||||||
if(up){
|
if(up != nil){
|
||||||
if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER){
|
if(ctl == vclock){
|
||||||
/* delaysched set because we held a lock or because our quantum ended */
|
/* delaysched set because we held a lock or because our quantum ended */
|
||||||
if(up->delaysched)
|
if(up->delaysched)
|
||||||
sched();
|
sched();
|
||||||
|
@ -74,58 +73,28 @@ irqhandled(Ureg *ureg, int vno)
|
||||||
preempted();
|
preempted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vno < VectorPIC)
|
if(vno < VectorPIC || vno == VectorSYSCALL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
|
||||||
* An unknown interrupt.
|
|
||||||
* Check for a default IRQ7. This can happen when
|
|
||||||
* the IRQ input goes away before the acknowledge.
|
|
||||||
* In this case, a 'default IRQ7' is generated, but
|
|
||||||
* the corresponding bit in the ISR isn't set.
|
|
||||||
* In fact, just ignore all such interrupts.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* call all interrupt routines, just in case */
|
|
||||||
for(i = VectorPIC; i <= MaxIrqLAPIC; i++){
|
|
||||||
ctl = vctl[i];
|
|
||||||
if(ctl == nil)
|
|
||||||
continue;
|
|
||||||
if(!ctl->isintr)
|
|
||||||
continue;
|
|
||||||
for(v = ctl; v != nil; v = v->next){
|
|
||||||
if(v->f)
|
|
||||||
v->f(ureg, v->a);
|
|
||||||
}
|
|
||||||
/* should we do this? */
|
|
||||||
if(ctl->eoi)
|
|
||||||
ctl->eoi(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clear the interrupt */
|
|
||||||
i8259isr(vno);
|
|
||||||
|
|
||||||
if(0)print("cpu%d: spurious interrupt %d, last %d\n",
|
|
||||||
m->machno, vno, m->lastintr);
|
|
||||||
if(0)if(conf.nmach > 1){
|
|
||||||
for(i = 0; i < MAXMACH; i++){
|
|
||||||
Mach *mach;
|
|
||||||
|
|
||||||
if(active.machs[i] == 0)
|
|
||||||
continue;
|
|
||||||
mach = MACHP(i);
|
|
||||||
if(m->machno == mach->machno)
|
|
||||||
continue;
|
|
||||||
print(" cpu%d: last %d",
|
|
||||||
mach->machno, mach->lastintr);
|
|
||||||
}
|
|
||||||
print("\n");
|
|
||||||
}
|
|
||||||
m->spuriousintr++;
|
m->spuriousintr++;
|
||||||
|
if(arch->intrspurious != nil && (*arch->intrspurious)(vno) == 0)
|
||||||
|
return 1; /* false alarm */
|
||||||
|
|
||||||
|
iprint("cpu%d: spurious interrupt %d, last %d\n",
|
||||||
|
m->machno, vno, m->lastintr);
|
||||||
|
|
||||||
|
/* call all non-local interrupt routines, just in case */
|
||||||
|
for(i = VectorPIC; i < nelem(vctl); i++){
|
||||||
|
ctl = vctl[i];
|
||||||
|
if(ctl == nil || ctl == vclock || ctl->local)
|
||||||
|
continue;
|
||||||
|
for(v = ctl; v != nil; v = v->next)
|
||||||
|
(*v->f)(ureg, v->a);
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,28 +103,59 @@ trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
|
||||||
{
|
{
|
||||||
Vctl *v;
|
Vctl *v;
|
||||||
|
|
||||||
if(vno < 0 || vno >= VectorPIC)
|
if(f == nil){
|
||||||
panic("trapenable: vno %d", vno);
|
print("trapenable: nil handler for %d, for %s\n",
|
||||||
|
vno, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vno < 0 || vno >= VectorPIC){
|
||||||
|
print("trapenable: vno %d out of range", vno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if((v = xalloc(sizeof(Vctl))) == nil)
|
if((v = xalloc(sizeof(Vctl))) == nil)
|
||||||
panic("trapenable: out of memory");
|
panic("trapenable: out of memory");
|
||||||
v->tbdf = BUSUNKNOWN;
|
|
||||||
v->f = f;
|
v->f = f;
|
||||||
v->a = a;
|
v->a = a;
|
||||||
|
|
||||||
|
v->tbdf = BUSUNKNOWN;
|
||||||
|
v->irq = -1;
|
||||||
|
v->vno = vno;
|
||||||
|
v->cpu = -1;
|
||||||
|
|
||||||
strncpy(v->name, name, KNAMELEN-1);
|
strncpy(v->name, name, KNAMELEN-1);
|
||||||
v->name[KNAMELEN-1] = 0;
|
v->name[KNAMELEN-1] = 0;
|
||||||
|
|
||||||
ilock(&vctllock);
|
ilock(&vctllock);
|
||||||
if(vctl[vno])
|
if(vctl[vno] != nil){
|
||||||
v->next = vctl[vno]->next;
|
print("trapenable: vno %d assigned twice: %s %s\n",
|
||||||
|
vno, vctl[vno]->name, v->name);
|
||||||
|
iunlock(&vctllock);
|
||||||
|
xfree(v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
vctl[vno] = v;
|
vctl[vno] = v;
|
||||||
iunlock(&vctllock);
|
iunlock(&vctllock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Vctl*
|
||||||
|
delayfree(Vctl *v)
|
||||||
|
{
|
||||||
|
static Vctl *q[4];
|
||||||
|
static uint x;
|
||||||
|
Vctl *r;
|
||||||
|
|
||||||
|
r = q[x], q[x] = v;
|
||||||
|
x = (x+1) % nelem(q);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
|
intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
|
||||||
{
|
{
|
||||||
int vno;
|
Vctl **pv, *v;
|
||||||
Vctl *v;
|
|
||||||
|
|
||||||
if(f == nil){
|
if(f == nil){
|
||||||
print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
|
print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
|
||||||
|
@ -163,45 +163,63 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))
|
if(arch->intrirqno != nil)
|
||||||
irq = -1;
|
irq = (*arch->intrirqno)(irq, tbdf);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IRQ2 doesn't really exist, it's used to gang the interrupt
|
|
||||||
* controllers together. A device set to IRQ2 will appear on
|
|
||||||
* the second interrupt controller as IRQ9.
|
|
||||||
*/
|
|
||||||
if(irq == 2)
|
|
||||||
irq = 9;
|
|
||||||
|
|
||||||
if((v = xalloc(sizeof(Vctl))) == nil)
|
if((v = xalloc(sizeof(Vctl))) == nil)
|
||||||
panic("intrenable: out of memory");
|
panic("intrenable: out of memory");
|
||||||
v->isintr = 1;
|
|
||||||
v->irq = irq;
|
|
||||||
v->tbdf = tbdf;
|
|
||||||
v->f = f;
|
v->f = f;
|
||||||
v->a = a;
|
v->a = a;
|
||||||
|
|
||||||
|
v->tbdf = tbdf;
|
||||||
|
v->irq = irq;
|
||||||
|
v->vno = -1;
|
||||||
|
v->cpu = -1;
|
||||||
|
|
||||||
strncpy(v->name, name, KNAMELEN-1);
|
strncpy(v->name, name, KNAMELEN-1);
|
||||||
v->name[KNAMELEN-1] = 0;
|
v->name[KNAMELEN-1] = 0;
|
||||||
|
|
||||||
ilock(&vctllock);
|
ilock(&vctllock);
|
||||||
vno = arch->intrenable(v);
|
v->vno = (*arch->intrassign)(v);
|
||||||
if(vno == -1){
|
if(v->vno < VectorPIC || v->vno >= nelem(vctl)){
|
||||||
iunlock(&vctllock);
|
print("intrenable: couldn't assign irq %d, tbdf 0x%uX for %s\n",
|
||||||
print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
|
|
||||||
irq, tbdf, v->name);
|
irq, tbdf, v->name);
|
||||||
|
Unlockandfree:
|
||||||
|
iunlock(&vctllock);
|
||||||
|
if(v != nil)
|
||||||
xfree(v);
|
xfree(v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(vctl[vno]){
|
pv = &vctl[v->vno];
|
||||||
if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
|
if(*pv != nil){
|
||||||
panic("intrenable: handler: %s %s %#p %#p %#p %#p",
|
if((*pv)->isr != v->isr || (*pv)->eoi != v->eoi){
|
||||||
vctl[vno]->name, v->name,
|
print("intrenable: incompatible handler: %s %s %#p %#p %#p %#p\n",
|
||||||
vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
|
(*pv)->name, v->name,
|
||||||
v->next = vctl[vno];
|
(*pv)->isr, v->isr,
|
||||||
|
(*pv)->eoi, v->eoi);
|
||||||
|
goto Unlockandfree;
|
||||||
|
}
|
||||||
|
if(*pv == vclock)
|
||||||
|
pv = &vclock->next;
|
||||||
|
v->next = *pv;
|
||||||
|
}
|
||||||
|
if(strcmp(name, "clock") == 0)
|
||||||
|
vclock = v;
|
||||||
|
*pv = v;
|
||||||
|
if(v->enable != nil){
|
||||||
|
coherence();
|
||||||
|
if((*v->enable)(v, pv != &vctl[v->vno] || v->next != nil) < 0){
|
||||||
|
print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
|
||||||
|
irq, tbdf, v->name);
|
||||||
|
*pv = v->next;
|
||||||
|
if(v == vclock)
|
||||||
|
vclock = nil;
|
||||||
|
if(conf.nmach > 1)
|
||||||
|
v = delayfree(v);
|
||||||
|
goto Unlockandfree;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
vctl[vno] = v;
|
|
||||||
iunlock(&vctllock);
|
iunlock(&vctllock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,40 +229,47 @@ intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
|
||||||
Vctl **pv, *v;
|
Vctl **pv, *v;
|
||||||
int vno;
|
int vno;
|
||||||
|
|
||||||
if(irq == 2)
|
if(arch->intrirqno != nil)
|
||||||
irq = 9;
|
irq = (*arch->intrirqno)(irq, tbdf);
|
||||||
if(arch->intrvecno == nil || (tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))){
|
|
||||||
/*
|
if(irq != -1 && arch->intrvecno != nil) {
|
||||||
* on APIC machine, irq is pretty meaningless
|
vno = (*arch->intrvecno)(irq);
|
||||||
* and disabling a the vector is not implemented.
|
if(vno < VectorPIC || vno >= nelem(vctl)){
|
||||||
* however, we still want to remove the matching
|
|
||||||
* Vctl entry to prevent calling Vctl.f() with a
|
|
||||||
* stale Vctl.a pointer.
|
|
||||||
*/
|
|
||||||
irq = -1;
|
irq = -1;
|
||||||
vno = VectorPIC;
|
vno = VectorPIC;
|
||||||
} else {
|
|
||||||
vno = arch->intrvecno(irq);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
irq = -1;
|
||||||
|
vno = VectorPIC;
|
||||||
|
}
|
||||||
|
|
||||||
ilock(&vctllock);
|
ilock(&vctllock);
|
||||||
do {
|
do {
|
||||||
for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
|
for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
|
||||||
if(v->isintr && (v->irq == irq || irq == -1)
|
if((v->irq == irq || irq == -1)
|
||||||
&& v->tbdf == tbdf && v->f == f && v->a == a
|
&& v->tbdf == tbdf && v->f == f && v->a == a
|
||||||
&& strcmp(v->name, name) == 0)
|
&& strcmp(v->name, name) == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(v != nil){
|
if(v != nil){
|
||||||
if(v->disable != nil)
|
if(v->disable != nil){
|
||||||
(*v->disable)(v);
|
if((*v->disable)(v, pv != &vctl[vno] || v->next != nil) < 0){
|
||||||
*pv = v->next;
|
print("intrdisable: couldn't disable irq %d, tbdf 0x%uX for %s\n",
|
||||||
xfree(v);
|
irq, tbdf, name);
|
||||||
|
|
||||||
if(irq != -1 && vctl[vno] == nil && arch->intrdisable != nil)
|
|
||||||
arch->intrdisable(irq);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} while(irq == -1 && ++vno <= MaxVectorAPIC);
|
coherence();
|
||||||
|
}
|
||||||
|
*pv = v->next;
|
||||||
|
if(v == vclock)
|
||||||
|
vclock = nil;
|
||||||
|
if(conf.nmach > 1)
|
||||||
|
v = delayfree(v);
|
||||||
|
iunlock(&vctllock);
|
||||||
|
if(v != nil)
|
||||||
|
xfree(v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while(irq == -1 && ++vno < nelem(vctl));
|
||||||
iunlock(&vctllock);
|
iunlock(&vctllock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,8 +283,8 @@ irqallocread(Chan*, void *a, long n, vlong offset)
|
||||||
if(n < 0 || offset < 0)
|
if(n < 0 || offset < 0)
|
||||||
error(Ebadarg);
|
error(Ebadarg);
|
||||||
|
|
||||||
for(vno=0; vno<nelem(vctl); vno++){
|
for(vno = 0; vno < nelem(vctl); vno++){
|
||||||
for(v=vctl[vno]; v; v=v->next){
|
for(v = vctl[vno]; v != nil; v = v->next){
|
||||||
m = snprint(buf, sizeof(buf), "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
|
m = snprint(buf, sizeof(buf), "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
|
||||||
offset -= m;
|
offset -= m;
|
||||||
if(offset >= 0)
|
if(offset >= 0)
|
||||||
|
@ -274,40 +299,8 @@ irqallocread(Chan*, void *a, long n, vlong offset)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
nmienable(void)
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Hack: should be locked with NVRAM access.
|
|
||||||
*/
|
|
||||||
outb(0x70, 0x80); /* NMI latch clear */
|
|
||||||
outb(0x70, 0);
|
|
||||||
|
|
||||||
x = inb(0x61) & 0x07; /* Enable NMI */
|
|
||||||
outb(0x61, 0x0C|x);
|
|
||||||
outb(0x61, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nmihandler(Ureg *ureg, void*)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Don't re-enable, it confuses the crash dumps.
|
|
||||||
nmienable();
|
|
||||||
*/
|
|
||||||
iprint("cpu%d: nmi PC %#p, status %ux\n",
|
|
||||||
m->machno, ureg->pc, inb(0x61));
|
|
||||||
while(m->machno != 0)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
irqinit(void)
|
irqinit(void)
|
||||||
{
|
{
|
||||||
addarchfile("irqalloc", 0444, irqallocread, nil);
|
addarchfile("irqalloc", 0444, irqallocread, nil);
|
||||||
|
|
||||||
trapenable(VectorNMI, nmihandler, nil, "nmi");
|
|
||||||
nmienable();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#include "mp.h"
|
#include "mp.h"
|
||||||
#include "apbootstrap.i"
|
#include "apbootstrap.i"
|
||||||
|
|
||||||
|
extern void i8259init(void);
|
||||||
|
|
||||||
/* filled in by pcmpinit or acpiinit */
|
/* filled in by pcmpinit or acpiinit */
|
||||||
Bus* mpbus;
|
Bus* mpbus;
|
||||||
Bus* mpbuslast;
|
Bus* mpbuslast;
|
||||||
|
@ -291,23 +293,51 @@ allocvector(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mpintrenablex(Vctl* v, int tbdf)
|
ioapicirqenable(Vctl *v, int shared)
|
||||||
|
{
|
||||||
|
Aintr *aintr = v->aux;
|
||||||
|
int lo, hi;
|
||||||
|
|
||||||
|
if(shared)
|
||||||
|
return 0;
|
||||||
|
hi = v->cpu<<24;
|
||||||
|
lo = mpintrinit(aintr->bus, aintr->intr, v->vno, v->irq);
|
||||||
|
lo |= ApicPHYSICAL; /* no-op */
|
||||||
|
ioapicrdtw(aintr->apic, aintr->intr->intin, hi, lo);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ioapicirqdisable(Vctl *v, int shared)
|
||||||
|
{
|
||||||
|
Aintr *aintr = v->aux;
|
||||||
|
int lo, hi;
|
||||||
|
|
||||||
|
if(shared)
|
||||||
|
return 0;
|
||||||
|
hi = 0;
|
||||||
|
lo = ApicIMASK;
|
||||||
|
ioapicrdtw(aintr->apic, aintr->intr->intin, hi, lo);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mpintrassignx(Vctl* v, int tbdf)
|
||||||
{
|
{
|
||||||
Bus *bus;
|
Bus *bus;
|
||||||
|
Pcidev *pci;
|
||||||
Aintr *aintr;
|
Aintr *aintr;
|
||||||
Apic *apic;
|
int bno, dno, pin, irq, type, lo, hi, n;
|
||||||
Pcidev *pcidev;
|
|
||||||
int bno, dno, pin, hi, irq, lo, n, type, vno;
|
|
||||||
|
|
||||||
type = BUSTYPE(tbdf);
|
type = BUSTYPE(tbdf);
|
||||||
bno = BUSBNO(tbdf);
|
bno = BUSBNO(tbdf);
|
||||||
dno = BUSDNO(tbdf);
|
dno = BUSDNO(tbdf);
|
||||||
|
|
||||||
pin = 0;
|
pin = 0;
|
||||||
pcidev = nil;
|
pci = nil;
|
||||||
if(type == BusPCI){
|
if(type == BusPCI){
|
||||||
if(pcidev = pcimatchtbdf(tbdf))
|
if((pci = pcimatchtbdf(tbdf)) != nil)
|
||||||
pin = pcicfgr8(pcidev, PciINTP);
|
pin = pcicfgr8(pci, PciINTP);
|
||||||
} else if(type == BusISA)
|
} else if(type == BusISA)
|
||||||
bno = mpisabus;
|
bno = mpisabus;
|
||||||
|
|
||||||
|
@ -325,14 +355,14 @@ Findbus:
|
||||||
* by the MP or ACPI tables then walk up the bus translating interrupt
|
* by the MP or ACPI tables then walk up the bus translating interrupt
|
||||||
* pin to parent bus.
|
* pin to parent bus.
|
||||||
*/
|
*/
|
||||||
if(pcidev && pcidev->parent && pin > 0){
|
if(pci != nil && pci->parent != nil && pin > 0){
|
||||||
pin = ((dno+(pin-1))%4)+1;
|
pin = ((dno+(pin-1))%4)+1;
|
||||||
pcidev = pcidev->parent;
|
pci = pci->parent;
|
||||||
bno = BUSBNO(pcidev->tbdf);
|
bno = BUSBNO(pci->tbdf);
|
||||||
dno = BUSDNO(pcidev->tbdf);
|
dno = BUSDNO(pci->tbdf);
|
||||||
goto Findbus;
|
goto Findbus;
|
||||||
}
|
}
|
||||||
print("mpintrenable: can't find bus type %d, number %d\n", type, bno);
|
print("mpintrassign: can't find bus type %d, number %d\n", type, bno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,65 +376,56 @@ Findbus:
|
||||||
irq = (dno<<2)|(pin-1);
|
irq = (dno<<2)|(pin-1);
|
||||||
else
|
else
|
||||||
irq = -1;
|
irq = -1;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
irq = v->irq;
|
irq = v->irq;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find a matching interrupt entry from the list of interrupts
|
* Find a matching interrupt entry from the list of interrupts
|
||||||
* attached to this bus.
|
* attached to this bus.
|
||||||
*/
|
*/
|
||||||
for(aintr = bus->aintr; aintr; aintr = aintr->next){
|
for(aintr = bus->aintr; aintr != nil; aintr = aintr->next){
|
||||||
if(aintr->intr->irq != irq)
|
if(aintr->intr->irq != irq)
|
||||||
continue;
|
continue;
|
||||||
if(0){
|
|
||||||
PCMPintr* p = aintr->intr;
|
|
||||||
print("mpintrenablex: bus %d intin %d irq %d\n",
|
|
||||||
p->busno, p->intin, p->irq);
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* Check if already enabled. Multifunction devices may share
|
* Check if already enabled. Multifunction devices may share
|
||||||
* INT[A-D]# so, if already enabled, check the polarity matches
|
* INT[A-D]# so, if already enabled, check the polarity matches
|
||||||
* and the trigger is level.
|
* and the trigger is level.
|
||||||
*
|
|
||||||
* Should check the devices differ only in the function number,
|
|
||||||
* but that can wait for the planned enable/disable rewrite.
|
|
||||||
* The RDT read here is safe for now as currently interrupts
|
|
||||||
* are never disabled once enabled.
|
|
||||||
*/
|
*/
|
||||||
apic = aintr->apic;
|
ioapicrdtr(aintr->apic, aintr->intr->intin, &hi, &lo);
|
||||||
ioapicrdtr(apic, aintr->intr->intin, 0, &lo);
|
if(lo & ApicIMASK){
|
||||||
if(!(lo & ApicIMASK)){
|
v->vno = allocvector();
|
||||||
vno = lo & 0xFF;
|
v->cpu = mpintrcpu();
|
||||||
if(0) print("%s vector %d (!imask)\n", v->name, vno);
|
lo = mpintrinit(aintr->bus, aintr->intr, v->vno, v->irq);
|
||||||
n = mpintrinit(bus, aintr->intr, vno, v->irq);
|
|
||||||
n |= ApicPHYSICAL; /* no-op */
|
|
||||||
lo &= ~(ApicRemoteIRR|ApicDELIVS);
|
|
||||||
if(n != lo){
|
|
||||||
print("mpintrenable: multiple botch irq %d, tbdf %uX, lo %8.8uX, n %8.8uX\n",
|
|
||||||
v->irq, tbdf, lo, n);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
v->isr = lapicisr;
|
|
||||||
v->eoi = lapiceoi;
|
|
||||||
return vno;
|
|
||||||
}
|
|
||||||
|
|
||||||
vno = allocvector();
|
|
||||||
hi = mpintrcpu()<<24;
|
|
||||||
lo = mpintrinit(bus, aintr->intr, vno, v->irq);
|
|
||||||
lo |= ApicPHYSICAL; /* no-op */
|
lo |= ApicPHYSICAL; /* no-op */
|
||||||
if(lo & ApicIMASK){
|
if(lo & ApicIMASK){
|
||||||
print("mpintrenable: disabled irq %d, tbdf %uX, lo %8.8uX, hi %8.8uX\n",
|
print("mpintrassign: disabled irq %d, tbdf %uX, lo %8.8uX, hi %8.8uX\n",
|
||||||
v->irq, tbdf, lo, hi);
|
v->irq, v->tbdf, lo, hi);
|
||||||
return -1;
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v->vno = lo & 0xFF;
|
||||||
|
v->cpu = hi >> 24;
|
||||||
|
lo &= ~(ApicRemoteIRR|ApicDELIVS);
|
||||||
|
n = mpintrinit(aintr->bus, aintr->intr, v->vno, v->irq);
|
||||||
|
n |= ApicPHYSICAL; /* no-op */
|
||||||
|
if(lo != n){
|
||||||
|
print("mpintrassign: multiple botch irq %d, tbdf %uX, lo %8.8uX, n %8.8uX\n",
|
||||||
|
v->irq, v->tbdf, lo, n);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC)
|
|
||||||
ioapicrdtw(apic, aintr->intr->intin, hi, lo);
|
|
||||||
|
|
||||||
v->isr = lapicisr;
|
v->isr = lapicisr;
|
||||||
v->eoi = lapiceoi;
|
v->eoi = lapiceoi;
|
||||||
return vno;
|
|
||||||
|
if((aintr->apic->flags & PcmpEN) && aintr->apic->type == PcmpIOAPIC){
|
||||||
|
v->aux = aintr;
|
||||||
|
v->enable = ioapicirqenable;
|
||||||
|
v->disable = ioapicirqdisable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return v->vno;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -469,23 +490,29 @@ htmsienable(Pcidev *pdev)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
msiintrdisable(Vctl *v)
|
msiirqenable(Vctl *v, int)
|
||||||
{
|
{
|
||||||
Pcidev *pci;
|
Pcidev *pci = v->aux;
|
||||||
|
return pcimsienable(pci, 0xFEE00000ULL | (v->cpu << 12), v->vno | (1<<14));
|
||||||
|
}
|
||||||
|
|
||||||
if((pci = pcimatchtbdf(v->tbdf)) != nil)
|
static int
|
||||||
pcimsidisable(pci);
|
msiirqdisable(Vctl *v, int)
|
||||||
|
{
|
||||||
|
Pcidev *pci = v->aux;
|
||||||
|
return pcimsidisable(pci);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
msiintrenable(Vctl *v)
|
msiintrenable(Vctl *v)
|
||||||
{
|
{
|
||||||
int tbdf, vno, cpu;
|
|
||||||
Pcidev *pci;
|
Pcidev *pci;
|
||||||
|
int tbdf;
|
||||||
|
|
||||||
if(getconf("*nomsi") != nil)
|
if(getconf("*nomsi") != nil)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
tbdf = v->tbdf;
|
tbdf = v->tbdf;
|
||||||
if(tbdf == BUSUNKNOWN || BUSTYPE(tbdf) != BusPCI)
|
if(tbdf == BUSUNKNOWN || BUSTYPE(tbdf) != BusPCI)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -498,18 +525,21 @@ msiintrenable(Vctl *v)
|
||||||
return -1;
|
return -1;
|
||||||
if(pcimsidisable(pci) < 0)
|
if(pcimsidisable(pci) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
vno = allocvector();
|
|
||||||
cpu = mpintrcpu();
|
v->vno = allocvector();
|
||||||
if(pcimsienable(pci, 0xFEE00000ULL | (cpu << 12), vno | (1<<14)) < 0)
|
v->cpu = mpintrcpu();
|
||||||
return -1;
|
|
||||||
v->disable = msiintrdisable;
|
|
||||||
v->isr = lapicisr;
|
v->isr = lapicisr;
|
||||||
v->eoi = lapiceoi;
|
v->eoi = lapiceoi;
|
||||||
return vno;
|
|
||||||
|
v->aux = pci;
|
||||||
|
v->enable = msiirqenable;
|
||||||
|
v->disable = msiirqdisable;
|
||||||
|
|
||||||
|
return v->vno;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
mpintrenable(Vctl* v)
|
mpintrassign(Vctl* v)
|
||||||
{
|
{
|
||||||
int irq, tbdf, vno;
|
int irq, tbdf, vno;
|
||||||
|
|
||||||
|
@ -524,17 +554,18 @@ mpintrenable(Vctl* v)
|
||||||
* breakpoint and page-fault).
|
* breakpoint and page-fault).
|
||||||
*/
|
*/
|
||||||
tbdf = v->tbdf;
|
tbdf = v->tbdf;
|
||||||
if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1)
|
if(tbdf != BUSUNKNOWN && (vno = mpintrassignx(v, tbdf)) != -1)
|
||||||
return vno;
|
return vno;
|
||||||
|
|
||||||
irq = v->irq;
|
irq = v->irq;
|
||||||
if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){
|
if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){
|
||||||
|
v->local = 1;
|
||||||
if(irq != IrqSPURIOUS)
|
if(irq != IrqSPURIOUS)
|
||||||
v->isr = lapiceoi;
|
v->isr = lapiceoi;
|
||||||
return VectorPIC+irq;
|
return VectorPIC+irq;
|
||||||
}
|
}
|
||||||
if(irq < 0 || irq > MaxIrqPIC){
|
if(irq < 0 || irq > MaxIrqPIC){
|
||||||
print("mpintrenable: irq %d out of range\n", irq);
|
print("mpintrassign: irq %d out of range\n", irq);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,16 +581,16 @@ mpintrenable(Vctl* v)
|
||||||
* be compatible with ISA.
|
* be compatible with ISA.
|
||||||
*/
|
*/
|
||||||
if(mpeisabus != -1){
|
if(mpeisabus != -1){
|
||||||
vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0));
|
vno = mpintrassignx(v, MKBUS(BusEISA, 0, 0, 0));
|
||||||
if(vno != -1)
|
if(vno != -1)
|
||||||
return vno;
|
return vno;
|
||||||
}
|
}
|
||||||
if(mpisabus != -1){
|
if(mpisabus != -1){
|
||||||
vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0));
|
vno = mpintrassignx(v, MKBUS(BusISA, 0, 0, 0));
|
||||||
if(vno != -1)
|
if(vno != -1)
|
||||||
return vno;
|
return vno;
|
||||||
}
|
}
|
||||||
print("mpintrenable: out of choices eisa %d isa %d tbdf %uX irq %d\n",
|
print("mpintrassign: out of choices eisa %d isa %d tbdf %uX irq %d\n",
|
||||||
mpeisabus, mpisabus, v->tbdf, v->irq);
|
mpeisabus, mpisabus, v->tbdf, v->irq);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,7 @@ typedef struct Bus {
|
||||||
typedef struct Aintr {
|
typedef struct Aintr {
|
||||||
PCMPintr* intr;
|
PCMPintr* intr;
|
||||||
Apic* apic;
|
Apic* apic;
|
||||||
|
Bus* bus;
|
||||||
Aintr* next;
|
Aintr* next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,7 +250,7 @@ extern void lapictimerset(uvlong);
|
||||||
|
|
||||||
extern int mpintrinit(Bus*, PCMPintr*, int, int);
|
extern int mpintrinit(Bus*, PCMPintr*, int, int);
|
||||||
extern void mpinit(void);
|
extern void mpinit(void);
|
||||||
extern int mpintrenable(Vctl*);
|
extern int mpintrassign(Vctl*);
|
||||||
extern void mpshutdown(void);
|
extern void mpshutdown(void);
|
||||||
extern void mpstartap(Apic*);
|
extern void mpstartap(Apic*);
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,7 @@ link
|
||||||
misc
|
misc
|
||||||
pci pcipc
|
pci pcipc
|
||||||
|
|
||||||
|
archgeneric devkbd i8259 i8253
|
||||||
archacpi mp apic squidboy ec
|
archacpi mp apic squidboy ec
|
||||||
archmp mp apic squidboy
|
archmp mp apic squidboy
|
||||||
mtrr
|
mtrr
|
||||||
|
|
|
@ -66,6 +66,8 @@ trapinit(void)
|
||||||
{
|
{
|
||||||
irqinit();
|
irqinit();
|
||||||
|
|
||||||
|
nmienable();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Special traps.
|
* Special traps.
|
||||||
* Syscall() is called directly without going through trap().
|
* Syscall() is called directly without going through trap().
|
||||||
|
|
|
@ -254,13 +254,12 @@ struct PCArch
|
||||||
char* id;
|
char* id;
|
||||||
int (*ident)(void); /* this should be in the model */
|
int (*ident)(void); /* this should be in the model */
|
||||||
void (*reset)(void); /* this should be in the model */
|
void (*reset)(void); /* this should be in the model */
|
||||||
int (*serialpower)(int); /* 1 == on, 0 == off */
|
|
||||||
int (*modempower)(int); /* 1 == on, 0 == off */
|
|
||||||
|
|
||||||
void (*intrinit)(void);
|
void (*intrinit)(void);
|
||||||
int (*intrenable)(Vctl*);
|
int (*intrassign)(Vctl*);
|
||||||
|
int (*intrirqno)(int, int);
|
||||||
int (*intrvecno)(int);
|
int (*intrvecno)(int);
|
||||||
int (*intrdisable)(int);
|
int (*intrspurious)(int);
|
||||||
void (*introff)(void);
|
void (*introff)(void);
|
||||||
void (*intron)(void);
|
void (*intron)(void);
|
||||||
|
|
||||||
|
|
|
@ -63,13 +63,6 @@ void i8253init(void);
|
||||||
void i8253reset(void);
|
void i8253reset(void);
|
||||||
uvlong i8253read(uvlong*);
|
uvlong i8253read(uvlong*);
|
||||||
void i8253timerset(uvlong);
|
void i8253timerset(uvlong);
|
||||||
int i8259disable(int);
|
|
||||||
int i8259enable(Vctl*);
|
|
||||||
void i8259init(void);
|
|
||||||
int i8259isr(int);
|
|
||||||
void i8259on(void);
|
|
||||||
void i8259off(void);
|
|
||||||
int i8259vecno(int);
|
|
||||||
void idle(void);
|
void idle(void);
|
||||||
void idlehands(void);
|
void idlehands(void);
|
||||||
int inb(int);
|
int inb(int);
|
||||||
|
@ -109,6 +102,7 @@ char* mtrr(uvlong, uvlong, char *);
|
||||||
void mtrrclock(void);
|
void mtrrclock(void);
|
||||||
int mtrrprint(char *, long);
|
int mtrrprint(char *, long);
|
||||||
void mtrrsync(void);
|
void mtrrsync(void);
|
||||||
|
void nmienable(void);
|
||||||
void noteret(void);
|
void noteret(void);
|
||||||
uchar nvramread(int);
|
uchar nvramread(int);
|
||||||
void nvramwrite(int, uchar);
|
void nvramwrite(int, uchar);
|
||||||
|
|
|
@ -93,6 +93,7 @@ link
|
||||||
|
|
||||||
misc
|
misc
|
||||||
pci pcipc
|
pci pcipc
|
||||||
|
archgeneric devkbd i8259 i8253
|
||||||
archacpi mp apic squidboy ec
|
archacpi mp apic squidboy ec
|
||||||
archmp mp apic squidboy
|
archmp mp apic squidboy
|
||||||
mtrr
|
mtrr
|
||||||
|
|
|
@ -67,6 +67,8 @@ trapinit(void)
|
||||||
{
|
{
|
||||||
irqinit();
|
irqinit();
|
||||||
|
|
||||||
|
nmienable();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Special traps.
|
* Special traps.
|
||||||
* Syscall() is called directly without going through trap().
|
* Syscall() is called directly without going through trap().
|
||||||
|
|
|
@ -52,9 +52,7 @@ shutdown(void)
|
||||||
HYPERVISOR_shutdown(1);
|
HYPERVISOR_shutdown(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int xenintrenable(Vctl *v);
|
int xenintrassign(Vctl *v);
|
||||||
int xenintrvecno(int irq);
|
|
||||||
int xenintrdisable(int irq);
|
|
||||||
void xentimerenable(void);
|
void xentimerenable(void);
|
||||||
uvlong xentimerread(uvlong*);
|
uvlong xentimerread(uvlong*);
|
||||||
void xentimerset(uvlong);
|
void xentimerset(uvlong);
|
||||||
|
@ -64,16 +62,14 @@ PCArch archxen = {
|
||||||
.ident= identify,
|
.ident= identify,
|
||||||
.reset= shutdown,
|
.reset= shutdown,
|
||||||
.intrinit= intrinit,
|
.intrinit= intrinit,
|
||||||
.intrenable= xenintrenable,
|
.intrassign= xenintrassign,
|
||||||
.intrvecno= xenintrvecno,
|
|
||||||
.intrdisable= xenintrdisable,
|
|
||||||
.clockenable= xentimerenable,
|
.clockenable= xentimerenable,
|
||||||
.fastclock= xentimerread,
|
.fastclock= xentimerread,
|
||||||
.timerset= xentimerset,
|
.timerset= xentimerset,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Placeholders to satisfy external references in generic devarch.c
|
* Placeholders to satisfy external references in devarch.c
|
||||||
*/
|
*/
|
||||||
ulong getcr4(void) { return 0; }
|
ulong getcr4(void) { return 0; }
|
||||||
void putcr4(ulong) {}
|
void putcr4(ulong) {}
|
||||||
|
@ -83,19 +79,7 @@ ulong inl(int) { return 0; }
|
||||||
void outb(int, int) {}
|
void outb(int, int) {}
|
||||||
void outs(int, ushort) {}
|
void outs(int, ushort) {}
|
||||||
void outl(int, ulong) {}
|
void outl(int, ulong) {}
|
||||||
void i8042reset(void) {}
|
|
||||||
void i8253enable(void) {}
|
|
||||||
void i8253init(void) {}
|
|
||||||
void i8253link(void) {}
|
|
||||||
uvlong i8253read(uvlong*) { return 0; }
|
|
||||||
void i8253timerset(uvlong) {}
|
|
||||||
int i8259disable(int) { return 0; }
|
|
||||||
int i8259enable(Vctl*) { return 0; }
|
|
||||||
void i8259init(void) {}
|
|
||||||
int i8259isr(int) { return 0; }
|
|
||||||
void i8259on(void) {}
|
|
||||||
void i8259off(void) {}
|
|
||||||
int i8259vecno(int) { return 0; }
|
|
||||||
int mtrrprint(char*, long) { return 0; }
|
int mtrrprint(char*, long) { return 0; }
|
||||||
char* mtrr(uvlong, uvlong, char *) { return nil; }
|
char* mtrr(uvlong, uvlong, char *) { return nil; }
|
||||||
void mtrrsync(void) {}
|
void mtrrsync(void) {}
|
||||||
|
|
|
@ -39,13 +39,6 @@ void i8253init(void);
|
||||||
void i8253link(void);
|
void i8253link(void);
|
||||||
uvlong i8253read(uvlong*);
|
uvlong i8253read(uvlong*);
|
||||||
void i8253timerset(uvlong);
|
void i8253timerset(uvlong);
|
||||||
int i8259disable(int);
|
|
||||||
int i8259enable(Vctl*);
|
|
||||||
void i8259init(void);
|
|
||||||
int i8259isr(int);
|
|
||||||
void i8259on(void);
|
|
||||||
void i8259off(void);
|
|
||||||
int i8259vecno(int);
|
|
||||||
void idle(void);
|
void idle(void);
|
||||||
void idlehands(void);
|
void idlehands(void);
|
||||||
int inb(int);
|
int inb(int);
|
||||||
|
|
|
@ -71,6 +71,7 @@ OBJ=\
|
||||||
mmu.$O\
|
mmu.$O\
|
||||||
random.$O\
|
random.$O\
|
||||||
rdb.$O\
|
rdb.$O\
|
||||||
|
irq.$O\
|
||||||
trap.$O\
|
trap.$O\
|
||||||
$CONF.root.$O\
|
$CONF.root.$O\
|
||||||
$CONF.rootc.$O\
|
$CONF.rootc.$O\
|
||||||
|
|
|
@ -28,6 +28,9 @@ enum {
|
||||||
|
|
||||||
void noted(Ureg*, ulong);
|
void noted(Ureg*, ulong);
|
||||||
|
|
||||||
|
extern void irqinit(void);
|
||||||
|
extern int irqhandled(Ureg*, int);
|
||||||
|
|
||||||
static void debugbpt(Ureg*, void*);
|
static void debugbpt(Ureg*, void*);
|
||||||
static void fault386(Ureg*, void*);
|
static void fault386(Ureg*, void*);
|
||||||
static void safe_fault386(Ureg*, void*);
|
static void safe_fault386(Ureg*, void*);
|
||||||
|
@ -35,148 +38,6 @@ static void doublefault(Ureg*, void*);
|
||||||
static void unexpected(Ureg*, void*);
|
static void unexpected(Ureg*, void*);
|
||||||
static void _dumpstack(Ureg*);
|
static void _dumpstack(Ureg*);
|
||||||
|
|
||||||
static Lock vctllock;
|
|
||||||
static Vctl *vctl[256];
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
Ntimevec = 20 /* number of time buckets for each intr */
|
|
||||||
};
|
|
||||||
ulong intrtimes[256][Ntimevec];
|
|
||||||
|
|
||||||
void
|
|
||||||
intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
|
|
||||||
{
|
|
||||||
int vno;
|
|
||||||
Vctl *v;
|
|
||||||
|
|
||||||
/**/
|
|
||||||
SETUPLOG(dprint("intrenable: irq %d, f %p, a %p, tbdf 0x%x, name %s\n",
|
|
||||||
irq, f, a, tbdf, name);)
|
|
||||||
/**/
|
|
||||||
if(f == nil){
|
|
||||||
print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
|
|
||||||
irq, tbdf, name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
v = xalloc(sizeof(Vctl));
|
|
||||||
v->isintr = 1;
|
|
||||||
v->irq = irq;
|
|
||||||
v->tbdf = tbdf;
|
|
||||||
v->f = f;
|
|
||||||
v->a = a;
|
|
||||||
strncpy(v->name, name, KNAMELEN-1);
|
|
||||||
v->name[KNAMELEN-1] = 0;
|
|
||||||
|
|
||||||
ilock(&vctllock);
|
|
||||||
vno = arch->intrenable(v);
|
|
||||||
if(vno == -1){
|
|
||||||
iunlock(&vctllock);
|
|
||||||
print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
|
|
||||||
irq, tbdf, v->name);
|
|
||||||
xfree(v);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(vctl[vno]){
|
|
||||||
if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
|
|
||||||
panic("intrenable: handler: %s %s %p %p %p %p\n",
|
|
||||||
vctl[vno]->name, v->name,
|
|
||||||
vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
|
|
||||||
v->next = vctl[vno];
|
|
||||||
}
|
|
||||||
vctl[vno] = v;
|
|
||||||
SETUPLOG(dprint("INTRENABLE: vctl[%d] is %p\n", vno, vctl[vno]);)
|
|
||||||
iunlock(&vctllock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
|
|
||||||
{
|
|
||||||
Vctl **pv, *v;
|
|
||||||
int vno;
|
|
||||||
|
|
||||||
vno = arch->intrvecno(irq);
|
|
||||||
ilock(&vctllock);
|
|
||||||
for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
|
|
||||||
if(v->isintr && v->irq == irq
|
|
||||||
&& v->tbdf == tbdf && v->f == f && v->a == a
|
|
||||||
&& strcmp(v->name, name) == 0){
|
|
||||||
*pv = v->next;
|
|
||||||
xfree(v);
|
|
||||||
|
|
||||||
if(vctl[vno] == nil && arch->intrdisable != nil)
|
|
||||||
arch->intrdisable(irq);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iunlock(&vctllock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static long
|
|
||||||
irqallocread(Chan*, void *a, long n, vlong offset)
|
|
||||||
{
|
|
||||||
char buf[2*(11+1)+KNAMELEN+1+1];
|
|
||||||
int vno, m;
|
|
||||||
Vctl *v;
|
|
||||||
|
|
||||||
if(n < 0 || offset < 0)
|
|
||||||
error(Ebadarg);
|
|
||||||
|
|
||||||
for(vno=0; vno<nelem(vctl); vno++){
|
|
||||||
for(v=vctl[vno]; v; v=v->next){
|
|
||||||
m = snprint(buf, sizeof(buf), "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
|
|
||||||
offset -= m;
|
|
||||||
if(offset >= 0)
|
|
||||||
continue;
|
|
||||||
if(n > -offset)
|
|
||||||
n = -offset;
|
|
||||||
offset += m;
|
|
||||||
memmove(a, buf+offset, n);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
|
|
||||||
{
|
|
||||||
Vctl *v;
|
|
||||||
|
|
||||||
if(vno < 0 || vno >= VectorPIC)
|
|
||||||
panic("trapenable: vno %d\n", vno);
|
|
||||||
v = xalloc(sizeof(Vctl));
|
|
||||||
v->tbdf = BUSUNKNOWN;
|
|
||||||
v->f = f;
|
|
||||||
v->a = a;
|
|
||||||
strncpy(v->name, name, KNAMELEN);
|
|
||||||
v->name[KNAMELEN-1] = 0;
|
|
||||||
|
|
||||||
lock(&vctllock);
|
|
||||||
if(vctl[vno])
|
|
||||||
v->next = vctl[vno]->next;
|
|
||||||
vctl[vno] = v;
|
|
||||||
unlock(&vctllock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
nmienable(void)
|
|
||||||
{
|
|
||||||
/* leave this here in case plan 9 ever makes it to dom0 */
|
|
||||||
#ifdef NOWAY
|
|
||||||
/*
|
|
||||||
* Hack: should be locked with NVRAM access.
|
|
||||||
*/
|
|
||||||
outb(0x70, 0x80); /* NMI latch clear */
|
|
||||||
outb(0x70, 0);
|
|
||||||
|
|
||||||
x = inb(0x61) & 0x07; /* Enable NMI */
|
|
||||||
outb(0x61, 0x08|x);
|
|
||||||
outb(0x61, x);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we started out doing the 'giant bulk init' for all traps.
|
/* we started out doing the 'giant bulk init' for all traps.
|
||||||
* we're going to do them one-by-one since error analysis is
|
* we're going to do them one-by-one since error analysis is
|
||||||
* so much easier that way.
|
* so much easier that way.
|
||||||
|
@ -212,6 +73,8 @@ trapinit(void)
|
||||||
vaddr += 6;
|
vaddr += 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irqinit();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Special traps.
|
* Special traps.
|
||||||
* Syscall() is called directly without going through trap().
|
* Syscall() is called directly without going through trap().
|
||||||
|
@ -220,9 +83,6 @@ trapinit(void)
|
||||||
trapenable(VectorPF, fault386, 0, "fault386");
|
trapenable(VectorPF, fault386, 0, "fault386");
|
||||||
trapenable(Vector2F, doublefault, 0, "doublefault");
|
trapenable(Vector2F, doublefault, 0, "doublefault");
|
||||||
trapenable(Vector15, unexpected, 0, "unexpected");
|
trapenable(Vector15, unexpected, 0, "unexpected");
|
||||||
|
|
||||||
nmienable();
|
|
||||||
addarchfile("irqalloc", 0444, irqallocread, nil);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* excname[32] = {
|
static char* excname[32] = {
|
||||||
|
@ -260,27 +120,18 @@ static char* excname[32] = {
|
||||||
"31 (reserved)",
|
"31 (reserved)",
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
static int
|
||||||
* keep histogram of interrupt service times
|
usertrap(int vno)
|
||||||
*/
|
|
||||||
void
|
|
||||||
intrtime(Mach*, int vno)
|
|
||||||
{
|
{
|
||||||
ulong diff;
|
char buf[ERRMAX];
|
||||||
ulong x;
|
|
||||||
|
|
||||||
x = perfticks();
|
if(vno < nelem(excname)){
|
||||||
diff = x - m->perf.intrts;
|
spllo();
|
||||||
m->perf.intrts = x;
|
sprint(buf, "sys: trap: %s", excname[vno]);
|
||||||
|
postnote(up, 1, buf, NDebug);
|
||||||
m->perf.inintr += diff;
|
return 1;
|
||||||
if(up == nil && m->perf.inidle > diff)
|
}
|
||||||
m->perf.inidle -= diff;
|
return 0;
|
||||||
|
|
||||||
diff /= m->cpumhz*100; // quantum = 100µsec
|
|
||||||
if(diff >= Ntimevec)
|
|
||||||
diff = Ntimevec-1;
|
|
||||||
intrtimes[vno][diff]++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* go to user space */
|
/* go to user space */
|
||||||
|
@ -304,120 +155,27 @@ kexit(Ureg*)
|
||||||
* rather than directly vectoring the handler. However, this avoids a
|
* rather than directly vectoring the handler. However, this avoids a
|
||||||
* lot of code duplication and possible bugs. The only exception is
|
* lot of code duplication and possible bugs. The only exception is
|
||||||
* VectorSYSCALL.
|
* VectorSYSCALL.
|
||||||
* Trap is called with interrupts (and events) disabled via interrupt-gates.
|
* Trap is called with interrupts disabled via interrupt-gates.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
trap(Ureg* ureg)
|
trap(Ureg* ureg)
|
||||||
{
|
{
|
||||||
int clockintr, i, vno, user;
|
int vno, user;
|
||||||
char buf[ERRMAX];
|
|
||||||
Vctl *ctl, *v;
|
|
||||||
Mach *mach;
|
|
||||||
|
|
||||||
TRAPLOG(dprint("trap ureg %lux %lux\n", (ulong*)ureg, ureg->trap);)
|
user = userureg(ureg);
|
||||||
m->perf.intrts = perfticks();
|
|
||||||
user = (ureg->cs & 0xFFFF) == UESEL;
|
|
||||||
if(user){
|
if(user){
|
||||||
up->dbgreg = ureg;
|
up->dbgreg = ureg;
|
||||||
cycles(&up->kentry);
|
cycles(&up->kentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
clockintr = 0;
|
|
||||||
|
|
||||||
vno = ureg->trap;
|
vno = ureg->trap;
|
||||||
if(vno < 0 || vno >= 256)
|
if(!irqhandled(ureg, vno) && (!user || !usertrap(vno))){
|
||||||
panic("bad interrupt number %d\n", vno);
|
if(!user){
|
||||||
TRAPLOG(dprint("trap: vno is 0x%x, vctl[%d] is %p\n", vno, vno, vctl[vno]);)
|
/* early fault before trapinit() */
|
||||||
if(ctl = vctl[vno]){
|
if(vno == VectorPF)
|
||||||
INTRLOG(dprint("ctl is %p, isintr is %d\n", ctl, ctl->isintr);)
|
fault386(ureg, 0);
|
||||||
if(ctl->isintr){
|
|
||||||
m->intr++;
|
|
||||||
if(vno >= VectorPIC && vno != VectorSYSCALL)
|
|
||||||
m->lastintr = ctl->irq;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INTRLOG(dprint("ctl %p, isr %p\n", ctl, ctl->isr);)
|
|
||||||
if(ctl->isr)
|
|
||||||
ctl->isr(vno);
|
|
||||||
for(v = ctl; v != nil; v = v->next){
|
|
||||||
INTRLOG(dprint("ctl %p, f is %p\n", v, v->f);)
|
|
||||||
if(v->f)
|
|
||||||
v->f(ureg, v->a);
|
|
||||||
}
|
|
||||||
INTRLOG(dprint("ctl %p, eoi %p\n", ctl, ctl->eoi);)
|
|
||||||
if(ctl->eoi)
|
|
||||||
ctl->eoi(vno);
|
|
||||||
|
|
||||||
if(ctl->isintr){
|
|
||||||
intrtime(m, vno);
|
|
||||||
|
|
||||||
//if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER)
|
|
||||||
if (ctl->tbdf != BUSUNKNOWN && ctl->irq == VIRQ_TIMER)
|
|
||||||
clockintr = 1;
|
|
||||||
|
|
||||||
if(up && !clockintr)
|
|
||||||
preempted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(vno <= nelem(excname) && user){
|
|
||||||
spllo();
|
|
||||||
sprint(buf, "sys: trap: %s", excname[vno]);
|
|
||||||
postnote(up, 1, buf, NDebug);
|
|
||||||
}
|
|
||||||
else if(vno >= VectorPIC && vno != VectorSYSCALL){
|
|
||||||
/*
|
|
||||||
* An unknown interrupt.
|
|
||||||
* Check for a default IRQ7. This can happen when
|
|
||||||
* the IRQ input goes away before the acknowledge.
|
|
||||||
* In this case, a 'default IRQ7' is generated, but
|
|
||||||
* the corresponding bit in the ISR isn't set.
|
|
||||||
* In fact, just ignore all such interrupts.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* call all interrupt routines, just in case */
|
|
||||||
for(i = VectorPIC; i <= MaxIrqLAPIC; i++){
|
|
||||||
ctl = vctl[i];
|
|
||||||
if(ctl == nil)
|
|
||||||
continue;
|
|
||||||
if(!ctl->isintr)
|
|
||||||
continue;
|
|
||||||
for(v = ctl; v != nil; v = v->next){
|
|
||||||
if(v->f)
|
|
||||||
v->f(ureg, v->a);
|
|
||||||
}
|
|
||||||
/* should we do this? */
|
|
||||||
if(ctl->eoi)
|
|
||||||
ctl->eoi(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
iprint("cpu%d: spurious interrupt %d, last %d\n",
|
|
||||||
m->machno, vno, m->lastintr);
|
|
||||||
if(0)if(conf.nmach > 1){
|
|
||||||
for(i = 0; i < MAXMACH; i++){
|
|
||||||
if(active.machs[i] == 0)
|
|
||||||
continue;
|
|
||||||
mach = MACHP(i);
|
|
||||||
if(m->machno == mach->machno)
|
|
||||||
continue;
|
|
||||||
print(" cpu%d: last %d",
|
|
||||||
mach->machno, mach->lastintr);
|
|
||||||
}
|
|
||||||
print("\n");
|
|
||||||
}
|
|
||||||
m->spuriousintr++;
|
|
||||||
if(user)
|
|
||||||
kexit(ureg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if(vno == VectorNMI){
|
|
||||||
nmienable();
|
|
||||||
if(m->machno != 0){
|
|
||||||
print("cpu%d: PC %8.8luX\n",
|
|
||||||
m->machno, ureg->pc);
|
|
||||||
for(;;);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dumpregs(ureg);
|
dumpregs(ureg);
|
||||||
if(!user){
|
if(!user){
|
||||||
ureg->sp = (ulong)&ureg->sp;
|
ureg->sp = (ulong)&ureg->sp;
|
||||||
|
@ -425,18 +183,10 @@ trap(Ureg* ureg)
|
||||||
}
|
}
|
||||||
if(vno < nelem(excname))
|
if(vno < nelem(excname))
|
||||||
panic("%s", excname[vno]);
|
panic("%s", excname[vno]);
|
||||||
panic("unknown trap/intr: %d\n", vno);
|
panic("unknown trap/intr: %d", vno);
|
||||||
}
|
}
|
||||||
splhi();
|
splhi();
|
||||||
|
|
||||||
/* delaysched set because we held a lock or because our quantum ended */
|
|
||||||
if(up && up->delaysched && clockintr){
|
|
||||||
INTRLOG(dprint("calling sched in trap? \n");)
|
|
||||||
sched();
|
|
||||||
INTRLOG(dprint("Back from calling sched in trap?\n");)
|
|
||||||
splhi();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(user){
|
if(user){
|
||||||
if(up->procctl || up->nnote)
|
if(up->procctl || up->nnote)
|
||||||
notify(ureg);
|
notify(ureg);
|
||||||
|
|
|
@ -389,6 +389,26 @@ xenupcall(Ureg *ureg)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xenirqenable(Vctl *v, int shared)
|
||||||
|
{
|
||||||
|
if(!shared){
|
||||||
|
uint port = v->vno-100;
|
||||||
|
HYPERVISOR_shared_info->evtchn_mask[port/32] &= ~(1<<(port%32));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xenirqdisable(Vctl *v, int shared)
|
||||||
|
{
|
||||||
|
if(!shared){
|
||||||
|
uint port = v->vno-100;
|
||||||
|
HYPERVISOR_shared_info->evtchn_mask[port/32] |= (1<<(port%32));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tbdf field is abused to distinguish virqs from channels:
|
* tbdf field is abused to distinguish virqs from channels:
|
||||||
*
|
*
|
||||||
|
@ -396,36 +416,29 @@ xenupcall(Ureg *ureg)
|
||||||
* tbdf=0 -> irq is a channel number
|
* tbdf=0 -> irq is a channel number
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xenintrenable(Vctl *v)
|
xenintrassign(Vctl *v)
|
||||||
{
|
{
|
||||||
evtchn_op_t op;
|
evtchn_op_t op;
|
||||||
uint port;
|
uint port;
|
||||||
|
|
||||||
/* XXX locking? */
|
|
||||||
if (v->tbdf != BUSUNKNOWN) {
|
if (v->tbdf != BUSUNKNOWN) {
|
||||||
op.cmd = EVTCHNOP_bind_virq;
|
op.cmd = EVTCHNOP_bind_virq;
|
||||||
op.u.bind_virq.virq = v->irq;
|
op.u.bind_virq.virq = v->irq;
|
||||||
op.u.bind_virq.vcpu = m->machno;
|
op.u.bind_virq.vcpu = m->machno;
|
||||||
if(HYPERVISOR_event_channel_op(&op) != 0)
|
if(HYPERVISOR_event_channel_op(&op) != 0){
|
||||||
panic("xenintrenable: bind %d failed", v->irq);
|
print("xenintrenable: bind %d failed", v->irq);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
port = op.u.bind_virq.port;
|
port = op.u.bind_virq.port;
|
||||||
} else
|
} else
|
||||||
port = v->irq;
|
port = v->irq;
|
||||||
if (port > 155)
|
if (port > 155)
|
||||||
return -1;
|
return -1;
|
||||||
HYPERVISOR_shared_info->evtchn_mask[port/32] &= ~(1<<(port%32));
|
v->enable = xenirqenable;
|
||||||
if(0)print("xenintrenable %s: irq %d port %d mask[%d] = %#lux\n", v->name, v->irq, port, port/32, HYPERVISOR_shared_info->evtchn_mask[port/32]);
|
v->disable = xenirqdisable;
|
||||||
return 100+port;
|
return 100+port;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
xenintrdisable(int irq)
|
|
||||||
{
|
|
||||||
USED(irq);
|
|
||||||
panic("xenintrdisable notyet\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
xenintrvecno(int irq)
|
xenintrvecno(int irq)
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,7 +68,7 @@ xentimerclock(Ureg* ureg, void*)
|
||||||
void
|
void
|
||||||
xentimerenable(void)
|
xentimerenable(void)
|
||||||
{
|
{
|
||||||
intrenable(VIRQ_TIMER, xentimerclock, nil, 0, "Xen Timer");
|
intrenable(VIRQ_TIMER, xentimerclock, nil, 0, "clock");
|
||||||
}
|
}
|
||||||
|
|
||||||
uvlong
|
uvlong
|
||||||
|
|
Loading…
Reference in a new issue