2011-03-30 12:46:40 +00:00
|
|
|
#include "u.h"
|
|
|
|
#include "../port/lib.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dat.h"
|
|
|
|
#include "fns.h"
|
|
|
|
#include "io.h"
|
|
|
|
|
|
|
|
#include "mp.h"
|
|
|
|
|
|
|
|
enum { /* Local APIC registers */
|
|
|
|
LapicID = 0x0020, /* ID */
|
|
|
|
LapicVER = 0x0030, /* Version */
|
|
|
|
LapicTPR = 0x0080, /* Task Priority */
|
|
|
|
LapicAPR = 0x0090, /* Arbitration Priority */
|
|
|
|
LapicPPR = 0x00A0, /* Processor Priority */
|
|
|
|
LapicEOI = 0x00B0, /* EOI */
|
|
|
|
LapicLDR = 0x00D0, /* Logical Destination */
|
|
|
|
LapicDFR = 0x00E0, /* Destination Format */
|
|
|
|
LapicSVR = 0x00F0, /* Spurious Interrupt Vector */
|
|
|
|
LapicISR = 0x0100, /* Interrupt Status (8 registers) */
|
|
|
|
LapicTMR = 0x0180, /* Trigger Mode (8 registers) */
|
|
|
|
LapicIRR = 0x0200, /* Interrupt Request (8 registers) */
|
|
|
|
LapicESR = 0x0280, /* Error Status */
|
|
|
|
LapicICRLO = 0x0300, /* Interrupt Command */
|
|
|
|
LapicICRHI = 0x0310, /* Interrupt Command [63:32] */
|
|
|
|
LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */
|
|
|
|
LapicPCINT = 0x0340, /* Performance Counter LVT */
|
|
|
|
LapicLINT0 = 0x0350, /* Local Vector Table 1 (LINT0) */
|
|
|
|
LapicLINT1 = 0x0360, /* Local Vector Table 2 (LINT1) */
|
|
|
|
LapicERROR = 0x0370, /* Local Vector Table 3 (ERROR) */
|
|
|
|
LapicTICR = 0x0380, /* Timer Initial Count */
|
|
|
|
LapicTCCR = 0x0390, /* Timer Current Count */
|
|
|
|
LapicTDCR = 0x03E0, /* Timer Divide Configuration */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum { /* LapicSVR */
|
|
|
|
LapicENABLE = 0x00000100, /* Unit Enable */
|
|
|
|
LapicFOCUS = 0x00000200, /* Focus Processor Checking Disable */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum { /* LapicICRLO */
|
|
|
|
/* [14] IPI Trigger Mode Level (RW) */
|
|
|
|
LapicDEASSERT = 0x00000000, /* Deassert level-sensitive interrupt */
|
|
|
|
LapicASSERT = 0x00004000, /* Assert level-sensitive interrupt */
|
|
|
|
|
|
|
|
/* [17:16] Remote Read Status */
|
|
|
|
LapicINVALID = 0x00000000, /* Invalid */
|
|
|
|
LapicWAIT = 0x00010000, /* In-Progress */
|
|
|
|
LapicVALID = 0x00020000, /* Valid */
|
|
|
|
|
|
|
|
/* [19:18] Destination Shorthand */
|
|
|
|
LapicFIELD = 0x00000000, /* No shorthand */
|
|
|
|
LapicSELF = 0x00040000, /* Self is single destination */
|
|
|
|
LapicALLINC = 0x00080000, /* All including self */
|
|
|
|
LapicALLEXC = 0x000C0000, /* All Excluding self */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum { /* LapicESR */
|
|
|
|
LapicSENDCS = 0x00000001, /* Send CS Error */
|
|
|
|
LapicRCVCS = 0x00000002, /* Receive CS Error */
|
|
|
|
LapicSENDACCEPT = 0x00000004, /* Send Accept Error */
|
|
|
|
LapicRCVACCEPT = 0x00000008, /* Receive Accept Error */
|
|
|
|
LapicSENDVECTOR = 0x00000020, /* Send Illegal Vector */
|
|
|
|
LapicRCVVECTOR = 0x00000040, /* Receive Illegal Vector */
|
|
|
|
LapicREGISTER = 0x00000080, /* Illegal Register Address */
|
|
|
|
};
|
|
|
|
|
|
|
|
enum { /* LapicTIMER */
|
|
|
|
/* [17] Timer Mode (RW) */
|
|
|
|
LapicONESHOT = 0x00000000, /* One-shot */
|
|
|
|
LapicPERIODIC = 0x00020000, /* Periodic */
|
|
|
|
|
|
|
|
/* [19:18] Timer Base (RW) */
|
|
|
|
LapicCLKIN = 0x00000000, /* use CLKIN as input */
|
|
|
|
LapicTMBASE = 0x00040000, /* use TMBASE */
|
|
|
|
LapicDIVIDER = 0x00080000, /* use output of the divider */
|
|
|
|
};
|
|
|
|
|
2012-06-24 03:34:17 +00:00
|
|
|
static uchar lapictdxtab[] = { /* LapicTDCR */
|
|
|
|
0x0B, /* divide by 1 */
|
|
|
|
0x00, /* divide by 2 */
|
|
|
|
0x01, /* divide by 4 */
|
|
|
|
0x02, /* divide by 8 */
|
|
|
|
0x03, /* divide by 16 */
|
|
|
|
0x08, /* divide by 32 */
|
|
|
|
0x09, /* divide by 64 */
|
|
|
|
0x0A, /* divide by 128 */
|
2011-03-30 12:46:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static ulong* lapicbase;
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
uvlong hz;
|
|
|
|
ulong max;
|
|
|
|
ulong min;
|
|
|
|
ulong div;
|
2012-06-24 03:34:17 +00:00
|
|
|
int tdx;
|
2011-03-30 12:46:40 +00:00
|
|
|
} lapictimer;
|
|
|
|
|
|
|
|
static ulong
|
|
|
|
lapicr(int r)
|
|
|
|
{
|
|
|
|
return *(lapicbase+(r/sizeof(*lapicbase)));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
lapicw(int r, ulong data)
|
|
|
|
{
|
|
|
|
*(lapicbase+(r/sizeof(*lapicbase))) = data;
|
|
|
|
data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
|
|
|
|
USED(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapiconline(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Reload the timer to de-synchronise the processors,
|
|
|
|
* then lower the task priority to allow interrupts to be
|
|
|
|
* accepted by the APIC.
|
|
|
|
*/
|
|
|
|
microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
|
|
|
|
lapicw(LapicTICR, lapictimer.max);
|
|
|
|
lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
|
|
|
|
|
2013-06-21 22:47:24 +00:00
|
|
|
/*
|
|
|
|
* not strickly neccesary, but reported (osdev.org) to be
|
|
|
|
* required for some machines.
|
|
|
|
*/
|
|
|
|
lapicw(LapicTDCR, lapictdxtab[lapictimer.tdx]);
|
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
lapicw(LapicTPR, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* use the i8253 clock to figure out our lapic timer rate.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
lapictimerinit(void)
|
|
|
|
{
|
2012-06-24 03:34:17 +00:00
|
|
|
Retry:
|
2011-03-30 12:46:40 +00:00
|
|
|
lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
|
2013-06-21 22:47:24 +00:00
|
|
|
lapicw(LapicTDCR, lapictdxtab[lapictimer.tdx]);
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
if(lapictimer.hz == 0ULL){
|
2012-06-24 03:34:17 +00:00
|
|
|
uvlong x, v, hz;
|
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
x = fastticks(&hz);
|
|
|
|
x += hz/10;
|
|
|
|
lapicw(LapicTICR, 0xffffffff);
|
|
|
|
do{
|
|
|
|
v = fastticks(nil);
|
|
|
|
}while(v < x);
|
|
|
|
|
2012-06-24 03:34:17 +00:00
|
|
|
v = (0xffffffffUL-lapicr(LapicTCCR))*10;
|
|
|
|
if(v > hz-(hz/10)){
|
|
|
|
if(v > hz+(hz/10) && lapictimer.tdx < nelem(lapictdxtab)-1){
|
|
|
|
lapictimer.tdx++;
|
|
|
|
goto Retry;
|
|
|
|
}
|
|
|
|
v = hz;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-06-24 03:34:17 +00:00
|
|
|
assert(v != 0);
|
|
|
|
|
|
|
|
lapictimer.hz = v;
|
2011-03-30 12:46:40 +00:00
|
|
|
lapictimer.div = hz/lapictimer.hz;
|
2012-06-24 03:34:17 +00:00
|
|
|
lapictimer.max = lapictimer.hz/HZ;
|
|
|
|
lapictimer.min = lapictimer.hz/(100*HZ);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicinit(Apic* apic)
|
|
|
|
{
|
|
|
|
ulong dfr, ldr, lvt;
|
|
|
|
|
|
|
|
if(lapicbase == 0)
|
|
|
|
lapicbase = apic->addr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These don't really matter in Physical mode;
|
|
|
|
* set the defaults anyway.
|
|
|
|
*/
|
|
|
|
if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
|
|
|
|
dfr = 0xf0000000;
|
|
|
|
else
|
|
|
|
dfr = 0xffffffff;
|
|
|
|
ldr = 0x00000000;
|
|
|
|
|
|
|
|
lapicw(LapicDFR, dfr);
|
|
|
|
lapicw(LapicLDR, ldr);
|
|
|
|
lapicw(LapicTPR, 0xff);
|
|
|
|
lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
|
|
|
|
|
|
|
|
lapictimerinit();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some Pentium revisions have a bug whereby spurious
|
|
|
|
* interrupts are generated in the through-local mode.
|
|
|
|
*/
|
|
|
|
switch(m->cpuidax & 0xFFF){
|
|
|
|
case 0x526: /* stepping cB1 */
|
|
|
|
case 0x52B: /* stepping E0 */
|
|
|
|
case 0x52C: /* stepping cC0 */
|
|
|
|
wrmsr(0x0E, 1<<14); /* TR12 */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the local interrupts. It's likely these should just be
|
|
|
|
* masked off for SMP mode as some Pentium Pros have problems if
|
|
|
|
* LINT[01] are set to ExtINT.
|
|
|
|
* Acknowledge any outstanding interrupts.
|
|
|
|
lapicw(LapicLINT0, apic->lintr[0]);
|
|
|
|
lapicw(LapicLINT1, apic->lintr[1]);
|
|
|
|
*/
|
|
|
|
lapiceoi(0);
|
|
|
|
|
|
|
|
lvt = (lapicr(LapicVER)>>16) & 0xFF;
|
|
|
|
if(lvt >= 4)
|
|
|
|
lapicw(LapicPCINT, ApicIMASK);
|
|
|
|
lapicw(LapicERROR, VectorPIC+IrqERROR);
|
|
|
|
lapicw(LapicESR, 0);
|
|
|
|
lapicr(LapicESR);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Issue an INIT Level De-Assert to synchronise arbitration ID's.
|
|
|
|
*/
|
|
|
|
lapicw(LapicICRHI, 0);
|
|
|
|
lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
|
|
|
|
while(lapicr(LapicICRLO) & ApicDELIVS)
|
|
|
|
;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do not allow acceptance of interrupts until all initialisation
|
|
|
|
* for this processor is done. For the bootstrap processor this can be
|
|
|
|
* early duing initialisation. For the application processors this should
|
|
|
|
* be after the bootstrap processor has lowered priority and is accepting
|
|
|
|
* interrupts.
|
|
|
|
lapicw(LapicTPR, 0);
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicstartap(Apic* apic, int v)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
ulong crhi;
|
|
|
|
|
2012-08-25 12:06:42 +00:00
|
|
|
/* make apic's processor do a warm reset */
|
2011-03-30 12:46:40 +00:00
|
|
|
crhi = apic->apicno<<24;
|
|
|
|
lapicw(LapicICRHI, crhi);
|
|
|
|
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
|
|
|
|
microdelay(200);
|
|
|
|
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
|
|
|
|
delay(10);
|
|
|
|
|
2012-08-25 12:06:42 +00:00
|
|
|
/* assumes apic is not an 82489dx */
|
2011-03-30 12:46:40 +00:00
|
|
|
for(i = 0; i < 2; i++){
|
|
|
|
lapicw(LapicICRHI, crhi);
|
2012-08-25 12:06:42 +00:00
|
|
|
/* make apic's processor start at v in real mode */
|
2011-03-30 12:46:40 +00:00
|
|
|
lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG));
|
|
|
|
microdelay(200);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicerror(Ureg*, void*)
|
|
|
|
{
|
|
|
|
ulong esr;
|
|
|
|
|
|
|
|
lapicw(LapicESR, 0);
|
|
|
|
esr = lapicr(LapicESR);
|
|
|
|
switch(m->cpuidax & 0xFFF){
|
|
|
|
case 0x526: /* stepping cB1 */
|
|
|
|
case 0x52B: /* stepping E0 */
|
|
|
|
case 0x52C: /* stepping cC0 */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicspurious(Ureg*, void*)
|
|
|
|
{
|
|
|
|
print("cpu%d: lapicspurious\n", m->machno);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
lapicisr(int v)
|
|
|
|
{
|
|
|
|
ulong isr;
|
|
|
|
|
|
|
|
isr = lapicr(LapicISR + (v/32));
|
|
|
|
|
|
|
|
return isr & (1<<(v%32));
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
lapiceoi(int v)
|
|
|
|
{
|
|
|
|
lapicw(LapicEOI, 0);
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicicrw(ulong hi, ulong lo)
|
|
|
|
{
|
|
|
|
lapicw(LapicICRHI, hi);
|
|
|
|
lapicw(LapicICRLO, lo);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
|
|
|
|
{
|
|
|
|
ulong *iowin;
|
|
|
|
|
|
|
|
iowin = apic->addr+(0x10/sizeof(ulong));
|
|
|
|
sel = IoapicRDT + 2*sel;
|
|
|
|
|
|
|
|
lock(apic);
|
|
|
|
*apic->addr = sel+1;
|
|
|
|
if(hi)
|
|
|
|
*hi = *iowin;
|
|
|
|
*apic->addr = sel;
|
|
|
|
if(lo)
|
|
|
|
*lo = *iowin;
|
|
|
|
unlock(apic);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ioapicrdtw(Apic* apic, int sel, int hi, int lo)
|
|
|
|
{
|
|
|
|
ulong *iowin;
|
|
|
|
|
|
|
|
iowin = apic->addr+(0x10/sizeof(ulong));
|
|
|
|
sel = IoapicRDT + 2*sel;
|
|
|
|
|
|
|
|
lock(apic);
|
|
|
|
*apic->addr = sel+1;
|
|
|
|
*iowin = hi;
|
|
|
|
*apic->addr = sel;
|
|
|
|
*iowin = lo;
|
|
|
|
unlock(apic);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ioapicinit(Apic* apic, int apicno)
|
|
|
|
{
|
|
|
|
int hi, lo, v;
|
|
|
|
ulong *iowin;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialise the I/O APIC.
|
|
|
|
* The MultiProcessor Specification says it is the responsibility
|
|
|
|
* of the O/S to set the APIC id.
|
|
|
|
* Make sure interrupts are all masked off for now.
|
|
|
|
*/
|
|
|
|
iowin = apic->addr+(0x10/sizeof(ulong));
|
|
|
|
lock(apic);
|
|
|
|
*apic->addr = IoapicVER;
|
|
|
|
apic->mre = (*iowin>>16) & 0xFF;
|
|
|
|
|
|
|
|
*apic->addr = IoapicID;
|
|
|
|
*iowin = apicno<<24;
|
|
|
|
unlock(apic);
|
|
|
|
|
|
|
|
hi = 0;
|
|
|
|
lo = ApicIMASK;
|
|
|
|
for(v = 0; v <= apic->mre; v++)
|
|
|
|
ioapicrdtw(apic, v, hi, lo);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapictimerset(uvlong next)
|
|
|
|
{
|
|
|
|
vlong period;
|
|
|
|
int x;
|
|
|
|
|
|
|
|
x = splhi();
|
|
|
|
lock(&m->apictimerlock);
|
|
|
|
|
|
|
|
period = lapictimer.max;
|
|
|
|
if(next != 0){
|
|
|
|
period = next - fastticks(nil);
|
|
|
|
period /= lapictimer.div;
|
|
|
|
|
|
|
|
if(period < lapictimer.min)
|
|
|
|
period = lapictimer.min;
|
|
|
|
else if(period > lapictimer.max - lapictimer.min)
|
|
|
|
period = lapictimer.max;
|
|
|
|
}
|
|
|
|
lapicw(LapicTICR, period);
|
|
|
|
|
|
|
|
unlock(&m->apictimerlock);
|
|
|
|
splx(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicclock(Ureg *u, void*)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* since the MTRR updates need to be synchronized across processors,
|
|
|
|
* we want to do this within the clock tick.
|
|
|
|
*/
|
|
|
|
mtrrclock();
|
|
|
|
timerintr(u, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicintron(void)
|
|
|
|
{
|
|
|
|
lapicw(LapicTPR, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicintroff(void)
|
|
|
|
{
|
|
|
|
lapicw(LapicTPR, 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicnmienable(void)
|
|
|
|
{
|
|
|
|
lapicw(LapicPCINT, ApicNMI);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapicnmidisable(void)
|
|
|
|
{
|
|
|
|
lapicw(LapicPCINT, ApicIMASK);
|
|
|
|
}
|