Add function to set tsc frequency directly, without calibration. Also tweak timer setup functions a bit: skip setup in case TimerPort has not the default value any more, i.e. another timer has been setup already.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/util.h | 1 + src/hw/timer.c | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/util.h b/src/util.h index d96db788d1b8..94592a2d2e8d 100644 --- a/src/util.h +++ b/src/util.h @@ -171,6 +171,7 @@ void sdcard_setup(void); // hw/timer.c void timer_setup(void); void pmtimer_setup(u16 ioport); +void tsctimer_setfreq(u32 khz, const char *src); u32 timer_calc(u32 msecs); u32 timer_calc_usec(u32 usecs); int timer_check(u32 end); diff --git a/src/hw/timer.c b/src/hw/timer.c index bdcb3bfca211..56bb289a4adc 100644 --- a/src/hw/timer.c +++ b/src/hw/timer.c @@ -101,8 +101,10 @@ tsctimer_setup(void) void timer_setup(void) { - if (!CONFIG_TSC_TIMER || (CONFIG_PMTIMER && TimerPort != PORT_PIT_COUNTER0)) + if (!CONFIG_TSC_TIMER) return; + if (TimerPort != PORT_PIT_COUNTER0) + return; // have timer already
// Check if CPU has a timestamp counter u32 eax, ebx, ecx, edx, cpuid_features = 0; @@ -113,11 +115,33 @@ timer_setup(void) tsctimer_setup(); }
+void +tsctimer_setfreq(u32 khz, const char *src) +{ + if (!CONFIG_TSC_TIMER) + return; + if (TimerPort != PORT_PIT_COUNTER0) + return; // have timer already + + TimerKHz = khz; + ShiftTSC = 0; + while (TimerKHz >= 6000) { + ShiftTSC++; + TimerKHz = (TimerKHz + 1) >> 1; + } + TimerPort = 0; + + dprintf(1, "CPU Mhz=%u (%s)\n", (TimerKHz << ShiftTSC) / 1000, src); +} + void pmtimer_setup(u16 ioport) { if (!CONFIG_PMTIMER) return; + if (TimerPort != PORT_PIT_COUNTER0) + return; // have timer already + dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport); TimerPort = ioport; TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000);
So we detect kvm even in case there is no qemu pci hostbridge present, for example when using the new, pci-less microvm machine type.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/fw/paravirt.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c index 4fcd8f570673..ef420931b9a5 100644 --- a/src/fw/paravirt.c +++ b/src/fw/paravirt.c @@ -102,13 +102,13 @@ static void qemu_detect(void) dprintf(1, "Running on QEMU (unknown nb: %04x:%04x)\n", v, d); break; } - kvm_detect(); }
void qemu_preinit(void) { qemu_detect(); + kvm_detect();
if (!CONFIG_QEMU) return; @@ -118,12 +118,6 @@ qemu_preinit(void) return; }
- if (!runningOnQEMU()) { - dprintf(1, "Warning: No QEMU Northbridge found (isapc?)\n"); - PlatformRunningOn |= PF_QEMU; - kvm_detect(); - } - // On emulators, get memory size from nvram. u32 rs = ((rtc_read(CMOS_MEM_EXTMEM2_LOW) << 16) | (rtc_read(CMOS_MEM_EXTMEM2_HIGH) << 24));
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/fw/paravirt.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c index ef420931b9a5..1e3d6012700b 100644 --- a/src/fw/paravirt.c +++ b/src/fw/paravirt.c @@ -67,6 +67,11 @@ static void kvm_detect(void) if (strcmp(signature, "KVMKVMKVM") == 0) { dprintf(1, "Running on KVM\n"); PlatformRunningOn |= PF_KVM; + if (eax >= KVM_CPUID_SIGNATURE + 0x10) { + cpuid(KVM_CPUID_SIGNATURE + 0x10, &eax, &ebx, &ecx, &edx); + dprintf(1, "kvm: have invtsc, freq %u kHz\n", eax); + tsctimer_setfreq(eax, "invtsc"); + } } }
Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/fw/paravirt.h | 12 ++++++++++++ src/fw/paravirt.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+)
diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h index f7e1d4c551d9..4e2e993ba9d3 100644 --- a/src/fw/paravirt.h +++ b/src/fw/paravirt.h @@ -5,6 +5,18 @@ #include "biosvar.h" // GET_GLOBAL #include "romfile.h" // struct romfile_s
+// kvmclock +struct pvclock_vcpu_time_info { + u32 version; + u32 pad0; + u64 tsc_timestamp; + u64 system_time; + u32 tsc_to_system_mul; + s8 tsc_shift; + u8 flags; + u8 pad[2]; +} __attribute__((__packed__)); /* 32 bytes */ + // Types of paravirtualized platforms. #define PF_QEMU (1<<0) #define PF_XEN (1<<1) diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c index 1e3d6012700b..119280c574fd 100644 --- a/src/fw/paravirt.c +++ b/src/fw/paravirt.c @@ -75,6 +75,48 @@ static void kvm_detect(void) } }
+#define KVM_FEATURE_CLOCKSOURCE 0 +#define KVM_FEATURE_CLOCKSOURCE2 3 + +#define MSR_KVM_SYSTEM_TIME 0x12 +#define MSR_KVM_SYSTEM_TIME_NEW 0x4b564d01 + +#define PVCLOCK_TSC_STABLE_BIT (1 << 0) + +struct pvclock_vcpu_time_info *kvmclock; + +static void kvmclock_init(void) +{ + unsigned int eax, ebx, ecx, edx, msr; + + if (!runningOnKVM()) + return; + + cpuid(KVM_CPUID_SIGNATURE + 0x01, &eax, &ebx, &ecx, &edx); + if (eax & (1 << KVM_FEATURE_CLOCKSOURCE2)) + msr = MSR_KVM_SYSTEM_TIME_NEW; + else if (eax & (1 << KVM_FEATURE_CLOCKSOURCE)) + msr = MSR_KVM_SYSTEM_TIME; + else + return; + + kvmclock = memalign_low(sizeof(*kvmclock), 32); + memset(kvmclock, 0, sizeof(*kvmclock)); + u32 value = (u32)(kvmclock); + dprintf(1, "kvmclock: at 0x%x (msr 0x%x)\n", value, msr); + wrmsr(msr, value | 0x01); + + if (!(kvmclock->flags & PVCLOCK_TSC_STABLE_BIT)) + return; + u32 MHz = (1000 << 16) / (kvmclock->tsc_to_system_mul >> 16); + if (kvmclock->tsc_shift < 0) + MHz <<= -kvmclock->tsc_shift; + else + MHz >>= kvmclock->tsc_shift; + dprintf(1, "kvmclock: stable tsc, %d MHz\n", MHz); + tsctimer_setfreq(MHz * 1000, "kvmclock"); +} + static void qemu_detect(void) { if (!CONFIG_QEMU_HARDWARE) @@ -163,6 +205,8 @@ qemu_platform_setup(void) return; }
+ kvmclock_init(); + // Initialize pci pci_setup(); smm_device_setup();
On Tue, Mar 10, 2020 at 11:22:45AM +0100, Gerd Hoffmann wrote:
Add function to set tsc frequency directly, without calibration. Also tweak timer setup functions a bit: skip setup in case TimerPort has not the default value any more, i.e. another timer has been setup already.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
The series looks good to me.
-Kevin
On Tue, Mar 10, 2020 at 11:36:58AM -0400, Kevin O'Connor wrote:
On Tue, Mar 10, 2020 at 11:22:45AM +0100, Gerd Hoffmann wrote:
Add function to set tsc frequency directly, without calibration. Also tweak timer setup functions a bit: skip setup in case TimerPort has not the default value any more, i.e. another timer has been setup already.
Signed-off-by: Gerd Hoffmann kraxel@redhat.com
The series looks good to me.
Pushed now.
cheers, Gerd