mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-09-24 02:08:04 +00:00
hv/mod_timer: split tsc handling code from timer.
Generalize and split basic cpu cycle/tick routines from x86/timer: - Instead of rdstc(), use cpu_ticks() in generic code. - Instead of get_tsc_khz(), use cpu_tickrate() in generic code. - Include "common/ticks.h" instead of "x86/timer.h" in generic code. - CYCLES_PER_MS is renamed to TICKS_PER_MS. The x86 specific API rdstc() and get_tsc_khz(), as well as TSC_PER_MS are still available in arch/x86/tsc.h but only for x86 specific usage. Tracked-On: #5920 Signed-off-by: Rong Liu <rong2.liu@intel.com> Signed-off-by: Yi Liang <yi.liang@intel.com>
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
#include <ivshmem.h>
|
||||
#include <asm/rtcm.h>
|
||||
#include <reloc.h>
|
||||
#include <ticks.h>
|
||||
|
||||
#define CPU_UP_TIMEOUT 100U /* millisecond */
|
||||
#define CPU_DOWN_TIMEOUT 100U /* millisecond */
|
||||
@@ -49,7 +50,7 @@ static void init_keylocker(void);
|
||||
static void set_current_pcpu_id(uint16_t pcpu_id);
|
||||
static void print_hv_banner(void);
|
||||
static uint16_t get_pcpu_id_from_lapic_id(uint32_t lapic_id);
|
||||
static uint64_t start_tsc __attribute__((__section__(".bss_noinit")));
|
||||
static uint64_t start_tick __attribute__((__section__(".bss_noinit")));
|
||||
|
||||
/**
|
||||
* @pre phys_cpu_num <= MAX_PCPU_NUM
|
||||
@@ -124,7 +125,7 @@ void init_pcpu_pre(bool is_bsp)
|
||||
|
||||
if (is_bsp) {
|
||||
pcpu_id = BSP_CPU_ID;
|
||||
start_tsc = rdtsc();
|
||||
start_tick = cpu_ticks();
|
||||
|
||||
/* Get CPU capabilities thru CPUID, including the physical address bit
|
||||
* limit which is required for initializing paging.
|
||||
@@ -220,7 +221,7 @@ void init_pcpu_post(uint16_t pcpu_id)
|
||||
HV_FULL_VERSION,
|
||||
HV_BUILD_TIME, HV_BUILD_VERSION, HV_BUILD_TYPE,
|
||||
HV_DAILY_TAG, HV_BUILD_SCENARIO, HV_BUILD_BOARD,
|
||||
HV_BUILD_USER, HV_CONFIG_TOOL, ticks_to_us(start_tsc));
|
||||
HV_BUILD_USER, HV_CONFIG_TOOL, ticks_to_us(start_tick));
|
||||
|
||||
pr_acrnlog("API version %u.%u", HV_API_MAJOR_VERSION, HV_API_MINOR_VERSION);
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include <logmsg.h>
|
||||
#include <asm/vmx.h>
|
||||
#include <asm/guest/hyperv.h>
|
||||
#include <asm/tsc.h>
|
||||
|
||||
#define DBG_LEVEL_HYPERV 6U
|
||||
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include <asm/security.h>
|
||||
#include <logmsg.h>
|
||||
#include <asm/seed.h>
|
||||
#include <asm/tsc.h>
|
||||
|
||||
#define TRUSTY_VERSION 1U
|
||||
#define TRUSTY_VERSION_2 2U
|
||||
@@ -282,7 +283,7 @@ static bool setup_trusty_info(struct acrn_vcpu *vcpu, uint32_t mem_size, uint64_
|
||||
/* Prepare trusty startup param */
|
||||
startup_param.size_of_this_struct = sizeof(struct trusty_startup_param);
|
||||
startup_param.mem_size = mem_size;
|
||||
startup_param.tsc_per_ms = CYCLES_PER_MS;
|
||||
startup_param.tsc_per_ms = TSC_PER_MS;
|
||||
startup_param.trusty_mem_base = TRUSTY_EPT_REBASE_GPA;
|
||||
|
||||
/* According to trusty boot protocol, it will use RDI as the
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <asm/cpufeatures.h>
|
||||
#include <asm/vmx.h>
|
||||
#include <asm/sgx.h>
|
||||
#include <asm/tsc.h>
|
||||
#include <logmsg.h>
|
||||
|
||||
static inline const struct vcpuid_entry *local_find_vcpuid_entry(const struct acrn_vcpu *vcpu,
|
||||
|
@@ -46,6 +46,7 @@
|
||||
#include <trace.h>
|
||||
#include <logmsg.h>
|
||||
#include <asm/irq.h>
|
||||
#include <ticks.h>
|
||||
#include "vlapic_priv.h"
|
||||
|
||||
#define VLAPIC_VERBOS 0
|
||||
@@ -281,7 +282,7 @@ static void vlapic_reset_timer(struct acrn_vlapic *vlapic)
|
||||
static bool
|
||||
set_expiration(struct acrn_vlapic *vlapic)
|
||||
{
|
||||
uint64_t now = rdtsc();
|
||||
uint64_t now = cpu_ticks();
|
||||
uint64_t delta;
|
||||
struct vlapic_timer *vtimer;
|
||||
struct hv_timer *timer;
|
||||
@@ -332,7 +333,7 @@ static void vlapic_update_lvtt(struct acrn_vlapic *vlapic,
|
||||
|
||||
static uint32_t vlapic_get_ccr(const struct acrn_vlapic *vlapic)
|
||||
{
|
||||
uint64_t now = rdtsc();
|
||||
uint64_t now = cpu_ticks();
|
||||
uint32_t remain_count = 0U;
|
||||
const struct vlapic_timer *vtimer;
|
||||
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include <asm/guest/nested.h>
|
||||
#include <asm/cpufeatures.h>
|
||||
#include <asm/rdt.h>
|
||||
#include <asm/tsc.h>
|
||||
#include <trace.h>
|
||||
#include <logmsg.h>
|
||||
|
||||
|
@@ -14,21 +14,11 @@
|
||||
#include <softirq.h>
|
||||
#include <trace.h>
|
||||
#include <asm/irq.h>
|
||||
#include <ticks.h>
|
||||
|
||||
#define MAX_TIMER_ACTIONS 32U
|
||||
#define CAL_MS 10U
|
||||
#define MIN_TIMER_PERIOD_US 500U
|
||||
|
||||
static uint32_t tsc_khz;
|
||||
|
||||
uint64_t rdtsc(void)
|
||||
{
|
||||
uint32_t lo, hi;
|
||||
|
||||
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
|
||||
return ((uint64_t)hi << 32U) | lo;
|
||||
}
|
||||
|
||||
static void run_timer(const struct hv_timer *timer)
|
||||
{
|
||||
/* deadline = 0 means stop timer, we should skip */
|
||||
@@ -211,129 +201,6 @@ void timer_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t pit_calibrate_tsc(uint32_t cal_ms_arg)
|
||||
{
|
||||
#define PIT_TICK_RATE 1193182U
|
||||
#define PIT_TARGET 0x3FFFU
|
||||
#define PIT_MAX_COUNT 0xFFFFU
|
||||
|
||||
uint32_t cal_ms = cal_ms_arg;
|
||||
uint32_t initial_pit;
|
||||
uint16_t current_pit;
|
||||
uint32_t max_cal_ms;
|
||||
uint64_t current_tsc;
|
||||
uint8_t initial_pit_high, initial_pit_low;
|
||||
|
||||
max_cal_ms = ((PIT_MAX_COUNT - PIT_TARGET) * 1000U) / PIT_TICK_RATE;
|
||||
cal_ms = min(cal_ms, max_cal_ms);
|
||||
|
||||
/* Assume the 8254 delivers 18.2 ticks per second when 16 bits fully
|
||||
* wrap. This is about 1.193MHz or a clock period of 0.8384uSec
|
||||
*/
|
||||
initial_pit = (cal_ms * PIT_TICK_RATE) / 1000U;
|
||||
initial_pit += PIT_TARGET;
|
||||
initial_pit_high = (uint8_t)(initial_pit >> 8U);
|
||||
initial_pit_low = (uint8_t)initial_pit;
|
||||
|
||||
/* Port 0x43 ==> Control word write; Data 0x30 ==> Select Counter 0,
|
||||
* Read/Write least significant byte first, mode 0, 16 bits.
|
||||
*/
|
||||
|
||||
pio_write8(0x30U, 0x43U);
|
||||
pio_write8(initial_pit_low, 0x40U); /* Write LSB */
|
||||
pio_write8(initial_pit_high, 0x40U); /* Write MSB */
|
||||
|
||||
current_tsc = rdtsc();
|
||||
|
||||
do {
|
||||
/* Port 0x43 ==> Control word write; 0x00 ==> Select
|
||||
* Counter 0, Counter Latch Command, Mode 0; 16 bits
|
||||
*/
|
||||
pio_write8(0x00U, 0x43U);
|
||||
|
||||
current_pit = (uint16_t)pio_read8(0x40U); /* Read LSB */
|
||||
current_pit |= (uint16_t)pio_read8(0x40U) << 8U; /* Read MSB */
|
||||
/* Let the counter count down to PIT_TARGET */
|
||||
} while (current_pit > PIT_TARGET);
|
||||
|
||||
current_tsc = rdtsc() - current_tsc;
|
||||
|
||||
return (current_tsc / cal_ms) * 1000U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine TSC frequency via CPUID 0x15 and 0x16.
|
||||
*/
|
||||
static uint64_t native_calibrate_tsc(void)
|
||||
{
|
||||
uint64_t tsc_hz = 0UL;
|
||||
const struct cpuinfo_x86 *cpu_info = get_pcpu_info();
|
||||
|
||||
if (cpu_info->cpuid_level >= 0x15U) {
|
||||
uint32_t eax_denominator, ebx_numerator, ecx_hz, reserved;
|
||||
|
||||
cpuid_subleaf(0x15U, 0x0U, &eax_denominator, &ebx_numerator,
|
||||
&ecx_hz, &reserved);
|
||||
|
||||
if ((eax_denominator != 0U) && (ebx_numerator != 0U)) {
|
||||
tsc_hz = ((uint64_t) ecx_hz *
|
||||
ebx_numerator) / eax_denominator;
|
||||
}
|
||||
}
|
||||
|
||||
if ((tsc_hz == 0UL) && (cpu_info->cpuid_level >= 0x16U)) {
|
||||
uint32_t eax_base_mhz, ebx_max_mhz, ecx_bus_mhz, edx;
|
||||
|
||||
cpuid_subleaf(0x16U, 0x0U, &eax_base_mhz, &ebx_max_mhz, &ecx_bus_mhz, &edx);
|
||||
tsc_hz = (uint64_t) eax_base_mhz * 1000000U;
|
||||
}
|
||||
|
||||
return tsc_hz;
|
||||
}
|
||||
|
||||
void calibrate_tsc(void)
|
||||
{
|
||||
uint64_t tsc_hz;
|
||||
|
||||
tsc_hz = native_calibrate_tsc();
|
||||
if (tsc_hz == 0U) {
|
||||
tsc_hz = pit_calibrate_tsc(CAL_MS);
|
||||
}
|
||||
tsc_khz = (uint32_t)(tsc_hz / 1000UL);
|
||||
printf("%s, tsc_khz=%lu\n", __func__, tsc_khz);
|
||||
}
|
||||
|
||||
uint32_t get_tsc_khz(void)
|
||||
{
|
||||
return tsc_khz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frequency of TSC in KHz (where 1KHz = 1000Hz). Only valid after
|
||||
* calibrate_tsc() returns.
|
||||
*/
|
||||
|
||||
uint64_t us_to_ticks(uint32_t us)
|
||||
{
|
||||
return (((uint64_t)us * (uint64_t)tsc_khz) / 1000UL);
|
||||
}
|
||||
|
||||
uint64_t ticks_to_us(uint64_t ticks)
|
||||
{
|
||||
uint64_t us = 0UL;
|
||||
|
||||
if (tsc_khz != 0U) {
|
||||
us = (ticks * 1000UL) / (uint64_t)tsc_khz;
|
||||
}
|
||||
|
||||
return us;
|
||||
}
|
||||
|
||||
uint64_t ticks_to_ms(uint64_t ticks)
|
||||
{
|
||||
return ticks / (uint64_t)tsc_khz;
|
||||
}
|
||||
|
||||
void udelay(uint32_t us)
|
||||
{
|
||||
uint64_t dest_tsc, delta_tsc;
|
||||
|
124
hypervisor/arch/x86/tsc.c
Normal file
124
hypervisor/arch/x86/tsc.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <util.h>
|
||||
#include <asm/cpuid.h>
|
||||
#include <asm/cpu_caps.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/tsc.h>
|
||||
|
||||
#define CAL_MS 10U
|
||||
|
||||
static uint32_t tsc_khz;
|
||||
|
||||
static uint64_t pit_calibrate_tsc(uint32_t cal_ms_arg)
|
||||
{
|
||||
#define PIT_TICK_RATE 1193182U
|
||||
#define PIT_TARGET 0x3FFFU
|
||||
#define PIT_MAX_COUNT 0xFFFFU
|
||||
|
||||
uint32_t cal_ms = cal_ms_arg;
|
||||
uint32_t initial_pit;
|
||||
uint16_t current_pit;
|
||||
uint32_t max_cal_ms;
|
||||
uint64_t current_tsc;
|
||||
uint8_t initial_pit_high, initial_pit_low;
|
||||
|
||||
max_cal_ms = ((PIT_MAX_COUNT - PIT_TARGET) * 1000U) / PIT_TICK_RATE;
|
||||
cal_ms = min(cal_ms, max_cal_ms);
|
||||
|
||||
/* Assume the 8254 delivers 18.2 ticks per second when 16 bits fully
|
||||
* wrap. This is about 1.193MHz or a clock period of 0.8384uSec
|
||||
*/
|
||||
initial_pit = (cal_ms * PIT_TICK_RATE) / 1000U;
|
||||
initial_pit += PIT_TARGET;
|
||||
initial_pit_high = (uint8_t)(initial_pit >> 8U);
|
||||
initial_pit_low = (uint8_t)initial_pit;
|
||||
|
||||
/* Port 0x43 ==> Control word write; Data 0x30 ==> Select Counter 0,
|
||||
* Read/Write least significant byte first, mode 0, 16 bits.
|
||||
*/
|
||||
|
||||
pio_write8(0x30U, 0x43U);
|
||||
pio_write8(initial_pit_low, 0x40U); /* Write LSB */
|
||||
pio_write8(initial_pit_high, 0x40U); /* Write MSB */
|
||||
|
||||
current_tsc = rdtsc();
|
||||
|
||||
do {
|
||||
/* Port 0x43 ==> Control word write; 0x00 ==> Select
|
||||
* Counter 0, Counter Latch Command, Mode 0; 16 bits
|
||||
*/
|
||||
pio_write8(0x00U, 0x43U);
|
||||
|
||||
current_pit = (uint16_t)pio_read8(0x40U); /* Read LSB */
|
||||
current_pit |= (uint16_t)pio_read8(0x40U) << 8U; /* Read MSB */
|
||||
/* Let the counter count down to PIT_TARGET */
|
||||
} while (current_pit > PIT_TARGET);
|
||||
|
||||
current_tsc = rdtsc() - current_tsc;
|
||||
|
||||
return (current_tsc / cal_ms) * 1000U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine TSC frequency via CPUID 0x15 and 0x16.
|
||||
*/
|
||||
static uint64_t native_calibrate_tsc(void)
|
||||
{
|
||||
uint64_t tsc_hz = 0UL;
|
||||
const struct cpuinfo_x86 *cpu_info = get_pcpu_info();
|
||||
|
||||
if (cpu_info->cpuid_level >= 0x15U) {
|
||||
uint32_t eax_denominator, ebx_numerator, ecx_hz, reserved;
|
||||
|
||||
cpuid_subleaf(0x15U, 0x0U, &eax_denominator, &ebx_numerator,
|
||||
&ecx_hz, &reserved);
|
||||
|
||||
if ((eax_denominator != 0U) && (ebx_numerator != 0U)) {
|
||||
tsc_hz = ((uint64_t) ecx_hz *
|
||||
ebx_numerator) / eax_denominator;
|
||||
}
|
||||
}
|
||||
|
||||
if ((tsc_hz == 0UL) && (cpu_info->cpuid_level >= 0x16U)) {
|
||||
uint32_t eax_base_mhz, ebx_max_mhz, ecx_bus_mhz, edx;
|
||||
|
||||
cpuid_subleaf(0x16U, 0x0U, &eax_base_mhz, &ebx_max_mhz, &ecx_bus_mhz, &edx);
|
||||
tsc_hz = (uint64_t) eax_base_mhz * 1000000U;
|
||||
}
|
||||
|
||||
return tsc_hz;
|
||||
}
|
||||
|
||||
void calibrate_tsc(void)
|
||||
{
|
||||
uint64_t tsc_hz;
|
||||
|
||||
tsc_hz = native_calibrate_tsc();
|
||||
if (tsc_hz == 0U) {
|
||||
tsc_hz = pit_calibrate_tsc(CAL_MS);
|
||||
}
|
||||
tsc_khz = (uint32_t)(tsc_hz / 1000UL);
|
||||
}
|
||||
|
||||
uint32_t get_tsc_khz(void)
|
||||
{
|
||||
return tsc_khz;
|
||||
}
|
||||
|
||||
/* external API */
|
||||
|
||||
uint64_t cpu_ticks(void)
|
||||
{
|
||||
return rdtsc();
|
||||
}
|
||||
|
||||
uint32_t cpu_tickrate(void)
|
||||
{
|
||||
return tsc_khz;
|
||||
}
|
@@ -17,7 +17,7 @@
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/lapic.h>
|
||||
#include <asm/vtd.h>
|
||||
#include <asm/timer.h>
|
||||
#include <ticks.h>
|
||||
#include <logmsg.h>
|
||||
#include <asm/board.h>
|
||||
#include <asm/vm_config.h>
|
||||
@@ -248,11 +248,11 @@ static inline void dmar_wait_completion(const struct dmar_drhd_rt *dmar_unit, ui
|
||||
uint32_t mask, uint32_t pre_condition, uint32_t *status)
|
||||
{
|
||||
/* variable start isn't used when built as release version */
|
||||
__unused uint64_t start = rdtsc();
|
||||
__unused uint64_t start = cpu_ticks();
|
||||
|
||||
do {
|
||||
*status = iommu_read32(dmar_unit, offset);
|
||||
ASSERT(((rdtsc() - start) < CYCLES_PER_MS),
|
||||
ASSERT(((cpu_ticks() - start) < TICKS_PER_MS),
|
||||
"DMAR OP Timeout!");
|
||||
asm_pause();
|
||||
} while( (*status & mask) == pre_condition);
|
||||
@@ -564,9 +564,9 @@ static void dmar_issue_qi_request(struct dmar_drhd_rt *dmar_unit, struct dmar_en
|
||||
qi_status = DMAR_INV_STATUS_INCOMPLETE;
|
||||
iommu_write32(dmar_unit, DMAR_IQT_REG, dmar_unit->qi_tail);
|
||||
|
||||
start = rdtsc();
|
||||
start = cpu_ticks();
|
||||
while (qi_status != DMAR_INV_STATUS_COMPLETED) {
|
||||
if ((rdtsc() - start) > CYCLES_PER_MS) {
|
||||
if ((cpu_ticks() - start) > TICKS_PER_MS) {
|
||||
pr_err("DMAR OP Timeout! @ %s", __func__);
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user