pc, pc64: add minimal HPET driver to measure LAPIC and TSC frequencies

This adds the new function pointer PCArch.clockinit(),
which is a timer dependent initialization routine.
It also takes over the job of guesscpuhz(). This way, the
architecture ident code can switch between different
timers (i8253, HPET and XEN timer).
This commit is contained in:
cinap_lenrek 2021-01-17 21:21:12 +01:00
parent 999e98b9b8
commit a05bab362f
19 changed files with 259 additions and 110 deletions

View file

@ -180,6 +180,20 @@ maptables(void)
} }
} }
static Tbl*
findtable(char sig[4])
{
Tbl *t;
int i;
for(i=0; i<ntblmap; i++){
t = tblmap[i];
if(memcmp(t->sig, sig, 4) == 0)
return t;
}
return nil;
}
static Apic* static Apic*
findapic(int gsi, int *pintin) findapic(int gsi, int *pintin)
{ {
@ -569,13 +583,9 @@ acpiinit(void)
amlinit(); amlinit();
/* load DSDT */ /* load DSDT */
for(i=0; i<ntblmap; i++){ if((t = findtable("DSDT")) != nil){
t = tblmap[i]; amlintmask = (~0ULL) >> (t->rev <= 1)*32;
if(memcmp(t->sig, "DSDT", 4) == 0){ amlload(t->data, tbldlen(t));
amlintmask = (~0ULL) >> (t->rev <= 1)*32;
amlload(t->data, tbldlen(t));
break;
}
} }
/* load SSDT, there can be multiple tables */ /* load SSDT, there can be multiple tables */
@ -588,15 +598,10 @@ acpiinit(void)
/* set APIC mode */ /* set APIC mode */
amleval(amlwalk(amlroot, "_PIC"), "i", 1, nil); amleval(amlwalk(amlroot, "_PIC"), "i", 1, nil);
for(i=0; i<ntblmap; i++){ t = findtable("APIC");
t = tblmap[i]; if(t == nil)
if(memcmp(t->sig, "APIC", 4) == 0) panic("acpiinit: no MADT (APIC) table");
goto Foundapic;
}
panic("acpiinit: no MADT (APIC) table");
return;
Foundapic:
s = t->data; s = t->data;
e = s + tbldlen(t); e = s + tbldlen(t);
lapicbase = get32(s); s += 8; lapicbase = get32(s); s += 8;
@ -708,16 +713,12 @@ acpireset(void)
{ {
uchar *p; uchar *p;
Tbl *t; Tbl *t;
int i;
/* stop application processors */ /* stop application processors */
mpshutdown(); mpshutdown();
/* locate and write platform reset register */ /* locate and write platform reset register */
for(i=0; i < ntblmap; i++){ while((t = findtable("FACP")) != nil){
t = tblmap[i];
if(memcmp(t->sig, "FACP", 4) != 0)
continue;
if(get32(t->len) <= 128) if(get32(t->len) <= 128)
break; break;
p = (uchar*)t; p = (uchar*)t;
@ -735,6 +736,11 @@ acpireset(void)
static int identify(void); static int identify(void);
extern int i8259irqno(int, int); extern int i8259irqno(int, int);
extern void i8253init(void);
extern int hpetprobe(uvlong);
extern void hpetinit(void);
extern uvlong hpetread(uvlong*);
PCArch archacpi = { PCArch archacpi = {
.id= "ACPI", .id= "ACPI",
@ -745,6 +751,7 @@ PCArch archacpi = {
.intrirqno= i8259irqno, .intrirqno= i8259irqno,
.intron= lapicintron, .intron= lapicintron,
.introff= lapicintroff, .introff= lapicintroff,
.clockinit= i8253init,
.fastclock= i8253read, .fastclock= i8253read,
.timerset= lapictimerset, .timerset= lapictimerset,
}; };
@ -782,6 +789,7 @@ identify(void)
{ {
uvlong pa; uvlong pa;
char *cp; char *cp;
Tbl *t;
if((cp = getconf("*acpi")) == nil) if((cp = getconf("*acpi")) == nil)
return 1; return 1;
@ -799,12 +807,20 @@ identify(void)
maptables(); maptables();
addarchfile("acpitbls", 0444, readtbls, nil); addarchfile("acpitbls", 0444, readtbls, nil);
addarchfile("acpimem", 0600, readmem, writemem); addarchfile("acpimem", 0600, readmem, writemem);
if(strcmp(cp, "0") == 0) if(strcmp(cp, "0") == 0 || findtable("APIC") == nil)
return 1; return 1;
if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0) if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
return 1; return 1;
if(getconf("*nohpet") == nil
&& (t = findtable("HPET")) != nil
&& ((uchar*)t)[40] == 0
&& hpetprobe(get64((uchar*)t+44)) == 0){
archacpi.clockinit = hpetinit;
archacpi.fastclock = hpetread;
}
if(m->havetsc && getconf("*notsc") == nil) if(m->havetsc && getconf("*notsc") == nil)
archacpi.fastclock = tscticks; archacpi.fastclock = tscticks;
return 0; return 0;
} }

View file

@ -40,6 +40,41 @@ archreset(void)
idle(); idle();
} }
void
delay(int millisecs)
{
millisecs *= m->loopconst;
if(millisecs <= 0)
millisecs = 1;
aamloop(millisecs);
}
void
microdelay(int microsecs)
{
microsecs *= m->loopconst;
microsecs /= 1000;
if(microsecs <= 0)
microsecs = 1;
aamloop(microsecs);
}
/*
* performance measurement ticks. must be low overhead.
* doesn't have to count over a second.
*/
ulong
perfticks(void)
{
uvlong x;
if(m->havetsc)
cycles(&x);
else
x = 0;
return x;
}
PCArch archgeneric = { PCArch archgeneric = {
.id= "generic", .id= "generic",
.ident= 0, .ident= 0,
@ -53,6 +88,7 @@ PCArch archgeneric = {
.intron= i8259on, .intron= i8259on,
.introff= i8259off, .introff= i8259off,
.clockinit= i8253init,
.clockenable= i8253enable, .clockenable= i8253enable,
.fastclock= i8253read, .fastclock= i8253read,
.timerset= i8253timerset, .timerset= i8253timerset,

View file

@ -229,6 +229,7 @@ struct Mach
int lastintr; int lastintr;
int loopconst; int loopconst;
int aalcycles;
int cpumhz; int cpumhz;
uvlong cyclefreq; /* Frequency of user readable cycle counter */ uvlong cyclefreq; /* Frequency of user readable cycle counter */
@ -289,6 +290,7 @@ struct PCArch
void (*introff)(void); void (*introff)(void);
void (*intron)(void); void (*intron)(void);
void (*clockinit)(void);
void (*clockenable)(void); void (*clockenable)(void);
uvlong (*fastclock)(uvlong*); uvlong (*fastclock)(uvlong*);
void (*timerset)(uvlong); void (*timerset)(uvlong);

View file

@ -461,8 +461,6 @@ static X86type x86sis[] =
{ -1, -1, 23, "unknown", }, /* total default */ { -1, -1, 23, "unknown", }, /* total default */
}; };
static X86type *cputype;
static void simplecycles(uvlong*); static void simplecycles(uvlong*);
void (*cycles)(uvlong*) = simplecycles; void (*cycles)(uvlong*) = simplecycles;
void _cycles(uvlong*); /* in l.s */ void _cycles(uvlong*); /* in l.s */
@ -547,6 +545,7 @@ cpuidentify(void)
|| (t->family == -1)) || (t->family == -1))
break; break;
m->aalcycles = t->aalcycles;
m->cpuidtype = t->name; m->cpuidtype = t->name;
/* /*
@ -559,11 +558,6 @@ cpuidentify(void)
wrmsr(0x10, 0); wrmsr(0x10, 0);
} }
/*
* use i8253 to guess our cpu speed
*/
guesscpuhz(t->aalcycles);
/* /*
* If machine check exception, page size extensions or page global bit * If machine check exception, page size extensions or page global bit
* are supported enable them in CR4 and clear any other set extensions. * are supported enable them in CR4 and clear any other set extensions.
@ -690,7 +684,6 @@ cpuidentify(void)
fpuinit(); fpuinit();
cputype = t;
return t->family; return t->family;
} }
@ -702,7 +695,7 @@ cputyperead(Chan*, void *a, long n, vlong offset)
mhz = (m->cpuhz+999999)/1000000; mhz = (m->cpuhz+999999)/1000000;
snprint(str, sizeof(str), "%s %lud\n", cputype->name, mhz); snprint(str, sizeof(str), "%s %lud\n", m->cpuidtype, mhz);
return readstr(offset, a, n, str); return readstr(offset, a, n, str);
} }
@ -715,7 +708,7 @@ archctlread(Chan*, void *a, long nn, vlong offset)
p = buf = smalloc(READSTR); p = buf = smalloc(READSTR);
ep = p + READSTR; ep = p + READSTR;
p = seprint(p, ep, "cpu %s %lud%s\n", p = seprint(p, ep, "cpu %s %lud%s\n",
cputype->name, (ulong)(m->cpuhz+999999)/1000000, m->cpuidtype, (ulong)(m->cpuhz+999999)/1000000,
m->havepge ? " pge" : ""); m->havepge ? " pge" : "");
p = seprint(p, ep, "pge %s\n", getcr4()&0x80 ? "on" : "off"); p = seprint(p, ep, "pge %s\n", getcr4()&0x80 ? "on" : "off");
p = seprint(p, ep, "coherence "); p = seprint(p, ep, "coherence ");
@ -877,6 +870,8 @@ archinit(void)
arch->intrinit = knownarch[0]->intrinit; arch->intrinit = knownarch[0]->intrinit;
if(arch->intrassign == nil) if(arch->intrassign == nil)
arch->intrassign = knownarch[0]->intrassign; arch->intrassign = knownarch[0]->intrassign;
if(arch->clockinit == nil)
arch->clockinit = knownarch[0]->clockinit;
} }
/* /*

View file

@ -51,7 +51,6 @@ ulong getcr3(void);
ulong getcr4(void); ulong getcr4(void);
u32int getdr6(void); u32int getdr6(void);
char* getconf(char*); char* getconf(char*);
void guesscpuhz(int);
void halt(void); void halt(void);
void mwait(void*); void mwait(void*);
int i8042auxcmd(int); int i8042auxcmd(int);

126
sys/src/9/pc/hpet.c Normal file
View file

@ -0,0 +1,126 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
/*
* HPET timer
*
* The HPET timer is memory mapped which allows
* faster access compared to the classic i8253.
* This timer is not used to generate interrupts
* as we use the LAPIC timer for that.
* Its purpose is to measure the LAPIC timer
* and TSC frequencies.
*/
enum {
Cap = 0x00/4,
Period = 0x04/4,
Config = 0x10/4,
Isr = 0x20/4,
Ctrlo = 0xF0/4,
Ctrhi = 0xF4/4,
};
static struct {
Lock;
u32int *mmio;
uvlong last;
uvlong freq;
} hpet;
int
hpetprobe(uvlong pa)
{
u32int cap, period;
int mhz;
if((hpet.mmio = vmap(pa, 1024)) == nil)
return -1;
cap = hpet.mmio[Cap];
period = hpet.mmio[Period];
if(period == 0 || period > 0x05F4E100)
return -1;
hpet.freq = 1000000000000000ULL / period;
mhz = (hpet.freq + 500000) / 1000000;
print("HPET: %llux %.8ux %d MHz \n", pa, cap, mhz);
return 0;
}
static uvlong
hpetcpufreq(void)
{
u32int x, y;
uvlong a, b;
int loops;
ilock(&hpet);
for(loops = 1000;;loops += 1000){
cycles(&a);
x = hpet.mmio[Ctrlo];
aamloop(loops);
cycles(&b);
y = hpet.mmio[Ctrlo] - x;
if(y >= hpet.freq/HZ || loops >= 1000000)
break;
}
iunlock(&hpet);
if(m->havetsc && b > a){
b -= a;
m->cyclefreq = b * hpet.freq / y;
m->aalcycles = (b + loops-1) / loops;
return m->cyclefreq;
}
return (vlong)loops*m->aalcycles * hpet.freq / y;
}
void
hpetinit(void)
{
uvlong cpufreq;
if(m->machno != 0){
m->cpuhz = MACHP(0)->cpuhz;
m->cpumhz = MACHP(0)->cpumhz;
m->cyclefreq = MACHP(0)->cyclefreq;
m->loopconst = MACHP(0)->loopconst;
return;
}
/* start counting */
hpet.mmio[Config] |= 1;
/* measure loopconst for delay() and tsc frequencies */
cpufreq = hpetcpufreq();
m->loopconst = (cpufreq/1000)/m->aalcycles; /* AAM+LOOP's for 1 ms */
m->cpuhz = cpufreq;
/* round to the nearest megahz */
m->cpumhz = (cpufreq+500000)/1000000L;
if(m->cpumhz == 0)
m->cpumhz = 1;
}
uvlong
hpetread(uvlong *hz)
{
uvlong ticks;
if(hz != nil)
*hz = hpet.freq;
ilock(&hpet);
ticks = hpet.last;
ticks += hpet.mmio[Ctrlo] - (u32int)ticks;
hpet.last = ticks;
iunlock(&hpet);
return ticks;
}

View file

@ -115,28 +115,11 @@ i8253reset(void)
iunlock(&i8253); iunlock(&i8253);
} }
void static uvlong
i8253init(void) i8253cpufreq(void)
{
ioalloc(T0cntr, 4, 0, "i8253");
ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
i8253reset();
}
void
guesscpuhz(int aalcycles)
{ {
int loops, x, y; int loops, x, y;
uvlong a, b, cpufreq; uvlong a, b;
if(m->machno != 0){
m->cpuhz = MACHP(0)->cpuhz;
m->cpumhz = MACHP(0)->cpumhz;
m->cyclefreq = MACHP(0)->cyclefreq;
m->loopconst = MACHP(0)->loopconst;
return;
}
ilock(&i8253); ilock(&i8253);
for(loops = 1000;;loops += 1000) { for(loops = 1000;;loops += 1000) {
@ -175,21 +158,38 @@ guesscpuhz(int aalcycles)
if(x == 0) if(x == 0)
x = 1; x = 1;
/*
* figure out clock frequency and a loop multiplier for delay().
* n.b. counter goes up by 2*Freq
*/
cpufreq = (vlong)loops*((aalcycles*2*Freq)/x);
m->loopconst = (cpufreq/1000)/aalcycles; /* AAM+LOOP's for 1 ms */
/* a == b means virtualbox has confused us */
if(m->havetsc && b > a){ if(m->havetsc && b > a){
b -= a; b -= a;
b *= 2*Freq; m->cyclefreq = b * 2*Freq / x;
b /= x; m->aalcycles = (b + loops-1) / loops;
m->cyclefreq = b;
cpufreq = b; return m->cyclefreq;
} }
return (vlong)loops*m->aalcycles * 2*Freq / x;
}
void
i8253init(void)
{
uvlong cpufreq;
if(m->machno != 0){
m->cpuhz = MACHP(0)->cpuhz;
m->cpumhz = MACHP(0)->cpumhz;
m->cyclefreq = MACHP(0)->cyclefreq;
m->loopconst = MACHP(0)->loopconst;
return;
}
ioalloc(T0cntr, 4, 0, "i8253");
ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
i8253reset();
cpufreq = i8253cpufreq();
m->loopconst = (cpufreq/1000)/m->aalcycles; /* AAM+LOOP's for 1 ms */
m->cpuhz = cpufreq; m->cpuhz = cpufreq;
/* /*
@ -281,38 +281,3 @@ i8253read(uvlong *hz)
return ticks<<Tickshift; return ticks<<Tickshift;
} }
void
delay(int millisecs)
{
millisecs *= m->loopconst;
if(millisecs <= 0)
millisecs = 1;
aamloop(millisecs);
}
void
microdelay(int microsecs)
{
microsecs *= m->loopconst;
microsecs /= 1000;
if(microsecs <= 0)
microsecs = 1;
aamloop(microsecs);
}
/*
* performance measurement ticks. must be low overhead.
* doesn't have to count over a second.
*/
ulong
perfticks(void)
{
uvlong x;
if(m->havetsc)
cycles(&x);
else
x = 0;
return x;
}

View file

@ -31,10 +31,11 @@ main(void)
quotefmtinstall(); quotefmtinstall();
screeninit(); screeninit();
print("\nPlan 9\n"); print("\nPlan 9\n");
i8253init();
cpuidentify(); cpuidentify();
meminit0(); meminit0();
archinit(); archinit();
if(arch->clockinit)
arch->clockinit();
meminit(); meminit();
ramdiskinit(); ramdiskinit();
confinit(); confinit();

View file

@ -97,7 +97,7 @@ misc
pci pcipc pci pcipc
archgeneric devkbd i8259 i8253 archgeneric devkbd i8259 i8253
archacpi mp apic squidboy ec archacpi mp apic squidboy ec hpet
archmp mp apic squidboy archmp mp apic squidboy
mtrr mtrr

View file

@ -15,6 +15,8 @@ squidboy(Apic* apic)
machinit(); machinit();
mmuinit(); mmuinit();
cpuidentify(); cpuidentify();
if(arch->clockinit)
arch->clockinit();
cpuidprint(); cpuidprint();
syncclock(); syncclock();
active.machs[m->machno] = 1; active.machs[m->machno] = 1;

View file

@ -221,6 +221,7 @@ struct Mach
int lastintr; int lastintr;
int loopconst; int loopconst;
int aalcycles;
int cpumhz; int cpumhz;
uvlong cyclefreq; /* Frequency of user readable cycle counter */ uvlong cyclefreq; /* Frequency of user readable cycle counter */
@ -278,6 +279,7 @@ struct PCArch
void (*introff)(void); void (*introff)(void);
void (*intron)(void); void (*intron)(void);
void (*clockinit)(void);
void (*clockenable)(void); void (*clockenable)(void);
uvlong (*fastclock)(uvlong*); uvlong (*fastclock)(uvlong*);
void (*timerset)(uvlong); void (*timerset)(uvlong);

View file

@ -52,7 +52,6 @@ u64int getcr4(void);
u64int getxcr0(void); u64int getxcr0(void);
u64int getdr6(void); u64int getdr6(void);
char* getconf(char*); char* getconf(char*);
void guesscpuhz(int);
void halt(void); void halt(void);
void mwait(void*); void mwait(void*);
int i8042auxcmd(int); int i8042auxcmd(int);

View file

@ -183,10 +183,11 @@ main(void)
quotefmtinstall(); quotefmtinstall();
screeninit(); screeninit();
print("\nPlan 9\n"); print("\nPlan 9\n");
i8253init();
cpuidentify(); cpuidentify();
meminit0(); meminit0();
archinit(); archinit();
if(arch->clockinit)
arch->clockinit();
meminit(); meminit();
ramdiskinit(); ramdiskinit();
confinit(); confinit();

View file

@ -94,7 +94,7 @@ link
misc misc
pci pcipc pci pcipc
archgeneric devkbd i8259 i8253 archgeneric devkbd i8259 i8253
archacpi mp apic squidboy ec archacpi mp apic squidboy ec hpet
archmp mp apic squidboy archmp mp apic squidboy
mtrr mtrr

View file

@ -16,6 +16,8 @@ squidboy(Apic* apic)
machinit(); machinit();
mmuinit(); mmuinit();
cpuidentify(); cpuidentify();
if(arch->clockinit)
arch->clockinit();
cpuidprint(); cpuidprint();
syncclock(); syncclock();
active.machs[m->machno] = 1; active.machs[m->machno] = 1;

View file

@ -52,10 +52,11 @@ shutdown(void)
HYPERVISOR_shutdown(1); HYPERVISOR_shutdown(1);
} }
int xenintrassign(Vctl *v); extern int xenintrassign(Vctl *v);
void xentimerenable(void); extern void xentimerinit(void);
uvlong xentimerread(uvlong*); extern void xentimerenable(void);
void xentimerset(uvlong); extern uvlong xentimerread(uvlong*);
extern void xentimerset(uvlong);
PCArch archxen = { PCArch archxen = {
.id= "Xen", .id= "Xen",
@ -63,6 +64,7 @@ PCArch archxen = {
.reset= shutdown, .reset= shutdown,
.intrinit= intrinit, .intrinit= intrinit,
.intrassign= xenintrassign, .intrassign= xenintrassign,
.clockinit= xentimerinit,
.clockenable= xentimerenable, .clockenable= xentimerenable,
.fastclock= xentimerread, .fastclock= xentimerread,
.timerset= xentimerset, .timerset= xentimerset,

View file

@ -27,7 +27,6 @@ void (*fprestore)(FPsave*);
void (*fpsave)(FPsave*); void (*fpsave)(FPsave*);
ulong getcr4(void); ulong getcr4(void);
char* getconf(char*); char* getconf(char*);
void guesscpuhz(int);
void halt(void); void halt(void);
void mwait(void*); void mwait(void*);
void i8042reset(void); void i8042reset(void);

View file

@ -76,6 +76,8 @@ main(void)
// meminit() is not for us // meminit() is not for us
confinit(); confinit();
archinit(); archinit();
if(arch->clockinit)
arch->clockinit();
xinit(); xinit();
trapinit(); trapinit();
printinit(); printinit();

View file

@ -34,7 +34,7 @@ getshadow(void)
/* just get it from the shared info */ /* just get it from the shared info */
void void
guesscpuhz(int) // XXX no arg! xentimerinit(void)
{ {
vcpu_time_info_t *t; vcpu_time_info_t *t;