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).
front
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;