#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: identcpu_subr.c,v 1.16 2026/05/27 12:43:31 yamt Exp $");
#ifdef _KERNEL_OPT
#include "lapic.h"
#endif
#include <sys/param.h>
#ifdef _KERNEL
#include <sys/systm.h>
#include <x86/cpuvar.h>
#include <x86/apicvar.h>
#include <x86/cacheinfo.h>
#include <machine/cpufunc.h>
#include <machine/cputypes.h>
#include <machine/specialreg.h>
#else
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <x86/cacheinfo.h>
#include <x86/cpuvar.h>
#include "cpuctl.h"
#include "cpuctl_i386.h"
#endif
#ifdef _KERNEL
static uint64_t
tsc_freq_vmware_cpuid(struct cpu_info *ci)
{
uint32_t descs[4];
uint64_t freq;
if (cpu_max_hypervisor_cpuid < 0x40000010)
return 0;
x86_cpuid(0x40000010, descs);
freq = descs[0];
if (freq == 0)
return 0;
aprint_verbose(
"got tsc frequency from vmware compatible cpuid\n");
#if NLAPIC > 0
if (descs[1] > 0) {
aprint_verbose(
"got lapic frequency from vmware compatible cpuid\n");
lapic_per_second = descs[1] * 1000;
lapic_from_cpuid = true;
}
#endif
return freq * 1000;
}
#endif
static uint64_t
tsc_freq_cpuid(struct cpu_info *ci)
{
uint64_t freq = 0, khz;
uint32_t descs[4];
uint32_t denominator, numerator;
if (ci->ci_max_cpuid < 0x15)
return 0;
x86_cpuid(0x15, descs);
denominator = descs[0];
numerator = descs[1];
if ((denominator != 0) && (numerator != 0)) {
khz = 0;
if (descs[2] != 0)
khz = descs[2] / 1000;
else if (CPUID_TO_FAMILY(ci->ci_signature) == 6) {
switch (CPUID_TO_MODEL(ci->ci_signature)) {
case 0x55:
case 0x5f:
khz = 25000;
break;
case 0x4e:
case 0x5e:
case 0x8e:
case 0x9e:
khz = 24000;
break;
case 0x5c:
khz = 19200;
break;
default:
break;
}
}
freq = khz * 1000 * numerator / denominator;
if (ci->ci_max_cpuid >= 0x16) {
x86_cpuid(0x16, descs);
if (descs[0] != 0) {
aprint_verbose_dev(ci->ci_dev,
"CPU base freq %" PRIu64 " Hz\n",
(uint64_t)descs[0] * 1000000);
if (freq == 0) {
khz = (uint64_t)descs[0] * 1000
* denominator / numerator;
freq = (uint64_t)descs[0] * 1000000;
}
}
if (descs[1] != 0) {
aprint_verbose_dev(ci->ci_dev,
"CPU max freq %" PRIu64 " Hz\n",
(uint64_t)descs[1] * 1000000);
}
}
#if defined(_KERNEL) && NLAPIC > 0
if ((khz != 0) && (lapic_per_second == 0)) {
lapic_per_second = khz * 1000;
aprint_debug_dev(ci->ci_dev,
"lapic_per_second set to %" PRIu32 "\n",
lapic_per_second);
}
#endif
}
return freq;
}
uint64_t
cpu_tsc_freq_cpuid(struct cpu_info *ci)
{
uint64_t freq = 0;
#ifdef _KERNEL
if (vm_guest == VM_GUEST_VMWARE || vm_guest == VM_GUEST_KVM)
freq = tsc_freq_vmware_cpuid(ci);
#endif
if (freq == 0 && cpu_vendor == CPUVENDOR_INTEL)
freq = tsc_freq_cpuid(ci);
if (freq != 0)
aprint_verbose_dev(ci->ci_dev, "TSC freq CPUID %" PRIu64
" Hz\n", freq);
return freq;
}
const struct x86_cache_info *
cpu_cacheinfo_lookup(const struct x86_cache_info *cai, uint8_t desc)
{
int i;
for (i = 0; cai[i].cai_desc != 0; i++) {
if (cai[i].cai_desc == desc)
return &cai[i];
}
return NULL;
}
void
cpu_dcp_cacheinfo(struct cpu_info *ci, uint32_t leaf)
{
u_int descs[4];
int type, level, ways, partitions, linesize, sets, totalsize;
int caitype = -1;
int i;
for (i = 0; ; i++) {
x86_cpuid2(leaf, i, descs);
type = __SHIFTOUT(descs[0], CPUID_DCP_CACHETYPE);
if (type == CPUID_DCP_CACHETYPE_N)
break;
level = __SHIFTOUT(descs[0], CPUID_DCP_CACHELEVEL);
switch (level) {
case 1:
if (type == CPUID_DCP_CACHETYPE_I)
caitype = CAI_ICACHE;
else if (type == CPUID_DCP_CACHETYPE_D)
caitype = CAI_DCACHE;
else
caitype = -1;
break;
case 2:
if (type == CPUID_DCP_CACHETYPE_U)
caitype = CAI_L2CACHE;
else
caitype = -1;
break;
case 3:
if (type == CPUID_DCP_CACHETYPE_U)
caitype = CAI_L3CACHE;
else
caitype = -1;
break;
default:
caitype = -1;
break;
}
if (caitype == -1)
continue;
ways = __SHIFTOUT(descs[1], CPUID_DCP_WAYS) + 1;
partitions =__SHIFTOUT(descs[1], CPUID_DCP_PARTITIONS)
+ 1;
linesize = __SHIFTOUT(descs[1], CPUID_DCP_LINESIZE)
+ 1;
sets = descs[2] + 1;
totalsize = ways * partitions * linesize * sets;
ci->ci_cinfo[caitype].cai_totalsize = totalsize;
ci->ci_cinfo[caitype].cai_associativity = ways;
ci->ci_cinfo[caitype].cai_linesize = linesize;
}
}