ref: 9a4b98a86793f5bf055e85230d0b7d674eeaa22b
parent: 19155801895da6ddef6cb4f8a7bed00341cff884
author: 9ferno <gophone2015@gmail.com>
date: Tue Aug 10 11:10:59 EDT 2021
copied from 9front
--- a/os/pc/apic.c
+++ b/os/pc/apic.c
@@ -24,7 +24,7 @@
LapicICRLO = 0x0300, /* Interrupt Command */
LapicICRHI = 0x0310, /* Interrupt Command [63:32] */
LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */
- LapicPCINT = 0x0340, /* Performance COunter LVT */
+ 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) */
@@ -76,28 +76,32 @@
LapicDIVIDER = 0x00080000, /* use output of the divider */
};
-enum { /* LapicTDCR */
- LapicX2 = 0x00000000, /* divide by 2 */
- LapicX4 = 0x00000001, /* divide by 4 */
- LapicX8 = 0x00000002, /* divide by 8 */
- LapicX16 = 0x00000003, /* divide by 16 */
- LapicX32 = 0x00000008, /* divide by 32 */
- LapicX64 = 0x00000009, /* divide by 64 */
- LapicX128 = 0x0000000A, /* divide by 128 */
- LapicX1 = 0x0000000B, /* divide by 1 */
+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 */
};
static ulong* lapicbase;
-struct
+typedef struct Apictimer Apictimer;
+struct Apictimer
{
uvlong hz;
ulong max;
ulong min;
ulong div;
-} lapictimer;
+ int tdx;
+};
-static int
+static Apictimer lapictimer[MAXMACH];
+
+static ulong
lapicr(int r)
{
return *(lapicbase+(r/sizeof(*lapicbase)));
@@ -104,7 +108,7 @@
}
static void
-lapicw(int r, int data)
+lapicw(int r, ulong data)
{
*(lapicbase+(r/sizeof(*lapicbase))) = data;
data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
@@ -114,6 +118,10 @@
void
lapiconline(void)
{
+ Apictimer *a;
+
+ a = &lapictimer[m->machno];
+
/*
* Reload the timer to de-synchronise the processors,
* then lower the task priority to allow interrupts to be
@@ -120,54 +128,90 @@
* accepted by the APIC.
*/
microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
- lapicw(LapicTICR, lapictimer.max);
+ lapicw(LapicTICR, a->max);
lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
+ /*
+ * not strickly neccesary, but reported (osdev.org) to be
+ * required for some machines.
+ */
+ lapicw(LapicTDCR, lapictdxtab[a->tdx]);
+
lapicw(LapicTPR, 0);
}
/*
- * use the i8253 clock to figure out our lapic timer rate.
+ * use the i8253/tsc clock to figure out our lapic timer rate.
*/
static void
lapictimerinit(void)
{
uvlong x, v, hz;
+ Apictimer *a;
+ int s;
- v = m->cpuhz/1000;
- lapicw(LapicTDCR, LapicX1);
+ if(m->machno != 0){
+ lapictimer[m->machno] = lapictimer[0];
+ return;
+ }
+
+ s = splhi();
+ a = &lapictimer[m->machno];
+ a->tdx = 0;
+Retry:
lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
+ lapicw(LapicTDCR, lapictdxtab[a->tdx]);
- if(lapictimer.hz == 0ULL){
- x = fastticks(&hz);
- x += hz/10;
- lapicw(LapicTICR, 0xffffffff);
- do{
- v = fastticks(nil);
- }while(v < x);
+ x = fastticks(&hz);
+ x += hz/10;
+ lapicw(LapicTICR, 0xffffffff);
+ do{
+ v = fastticks(nil);
+ }while(v < x);
- lapictimer.hz = (0xffffffffUL-lapicr(LapicTCCR))*10;
- lapictimer.max = lapictimer.hz/HZ;
- lapictimer.min = lapictimer.hz/(100*HZ);
-
- if(lapictimer.hz > hz)
- panic("lapic clock faster than cpu clock");
- lapictimer.div = hz/lapictimer.hz;
+ v = (0xffffffffUL-lapicr(LapicTCCR))*10;
+ if(v > hz-(hz/10)){
+ if(v > hz+(hz/10) && a->tdx < nelem(lapictdxtab)-1){
+ a->tdx++;
+ goto Retry;
+ }
+ v = hz;
}
+
+ assert(v >= (100*HZ));
+
+ a->hz = v;
+ a->div = hz/a->hz;
+ a->max = a->hz/HZ;
+ a->min = a->hz/(100*HZ);
+
+ splx(s);
+
+ v = (v+500000LL)/1000000LL;
+ print("cpu%d: lapic clock at %lludMHz\n", m->machno, v);
}
void
lapicinit(Apic* apic)
{
- ulong r, lvt;
+ ulong dfr, ldr, lvt;
if(lapicbase == 0)
lapicbase = apic->addr;
- lapicw(LapicDFR, 0xFFFFFFFF);
- r = (lapicr(LapicID)>>24) & 0xFF;
- lapicw(LapicLDR, (1<<r)<<24);
- lapicw(LapicTPR, 0xFF);
+ /*
+ * 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();
@@ -222,8 +266,10 @@
void
lapicstartap(Apic* apic, int v)
{
- int crhi, i;
+ int i;
+ ulong crhi;
+ /* make apic's processor do a warm reset */
crhi = apic->apicno<<24;
lapicw(LapicICRHI, crhi);
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
@@ -231,8 +277,10 @@
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
delay(10);
+ /* assumes apic is not an 82489dx */
for(i = 0; i < 2; i++){
lapicw(LapicICRHI, crhi);
+ /* make apic's processor start at v in real mode */
lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG));
microdelay(200);
}
@@ -241,7 +289,7 @@
void
lapicerror(Ureg*, void*)
{
- int esr;
+ ulong esr;
lapicw(LapicESR, 0);
esr = lapicr(LapicESR);
@@ -251,7 +299,7 @@
case 0x52C: /* stepping cC0 */
return;
}
- print("cpu%d: lapicerror: 0x%8.8uX\n", m->machno, esr);
+ print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
}
void
@@ -263,7 +311,7 @@
int
lapicisr(int v)
{
- int isr;
+ ulong isr;
isr = lapicr(LapicISR + (v/32));
@@ -279,7 +327,7 @@
}
void
-lapicicrw(int hi, int lo)
+lapicicrw(ulong hi, ulong lo)
{
lapicw(LapicICRHI, hi);
lapicw(LapicICRLO, lo);
@@ -350,29 +398,49 @@
lapictimerset(uvlong next)
{
vlong period;
- int x;
+ Apictimer *a;
- 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;
- }
+ a = &lapictimer[m->machno];
+ period = next - fastticks(nil);
+ period /= a->div;
+ if(period < a->min)
+ period = a->min;
+ else if(period > a->max - a->min)
+ period = a->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);
}