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

View file

@ -40,6 +40,41 @@ archreset(void)
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 = {
.id= "generic",
.ident= 0,
@ -53,6 +88,7 @@ PCArch archgeneric = {
.intron= i8259on,
.introff= i8259off,
.clockinit= i8253init,
.clockenable= i8253enable,
.fastclock= i8253read,
.timerset= i8253timerset,

View file

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

View file

@ -461,8 +461,6 @@ static X86type x86sis[] =
{ -1, -1, 23, "unknown", }, /* total default */
};
static X86type *cputype;
static void simplecycles(uvlong*);
void (*cycles)(uvlong*) = simplecycles;
void _cycles(uvlong*); /* in l.s */
@ -547,6 +545,7 @@ cpuidentify(void)
|| (t->family == -1))
break;
m->aalcycles = t->aalcycles;
m->cpuidtype = t->name;
/*
@ -559,11 +558,6 @@ cpuidentify(void)
wrmsr(0x10, 0);
}
/*
* use i8253 to guess our cpu speed
*/
guesscpuhz(t->aalcycles);
/*
* If machine check exception, page size extensions or page global bit
* are supported enable them in CR4 and clear any other set extensions.
@ -690,7 +684,6 @@ cpuidentify(void)
fpuinit();
cputype = t;
return t->family;
}
@ -702,7 +695,7 @@ cputyperead(Chan*, void *a, long n, vlong offset)
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);
}
@ -715,7 +708,7 @@ archctlread(Chan*, void *a, long nn, vlong offset)
p = buf = smalloc(READSTR);
ep = p + READSTR;
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" : "");
p = seprint(p, ep, "pge %s\n", getcr4()&0x80 ? "on" : "off");
p = seprint(p, ep, "coherence ");
@ -877,6 +870,8 @@ archinit(void)
arch->intrinit = knownarch[0]->intrinit;
if(arch->intrassign == nil)
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);
u32int getdr6(void);
char* getconf(char*);
void guesscpuhz(int);
void halt(void);
void mwait(void*);
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);
}
void
i8253init(void)
{
ioalloc(T0cntr, 4, 0, "i8253");
ioalloc(T2ctl, 1, 0, "i8253.cntr2ctl");
i8253reset();
}
void
guesscpuhz(int aalcycles)
static uvlong
i8253cpufreq(void)
{
int loops, x, y;
uvlong a, b, 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;
}
uvlong a, b;
ilock(&i8253);
for(loops = 1000;;loops += 1000) {
@ -175,21 +158,38 @@ guesscpuhz(int aalcycles)
if(x == 0)
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){
b -= a;
b *= 2*Freq;
b /= x;
m->cyclefreq = b;
cpufreq = b;
m->cyclefreq = b * 2*Freq / x;
m->aalcycles = (b + loops-1) / loops;
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;
/*
@ -281,38 +281,3 @@ i8253read(uvlong *hz)
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();
screeninit();
print("\nPlan 9\n");
i8253init();
cpuidentify();
meminit0();
archinit();
if(arch->clockinit)
arch->clockinit();
meminit();
ramdiskinit();
confinit();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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