Home | History | Annotate | Line # | Download | only in x86
      1 /* $NetBSD: identcpu_subr.c,v 1.15 2025/05/16 04:35:54 imil Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2020 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Masanobu SAITOH.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * Subroutines for CPU.
     34  * This file is shared between kernel and userland.
     35  * See src/usr.sbin/cpuctl/{Makefile, arch/i386.c}).
     36  */
     37 #include <sys/cdefs.h>
     38 __KERNEL_RCSID(0, "$NetBSD: identcpu_subr.c,v 1.15 2025/05/16 04:35:54 imil Exp $");
     39 
     40 #ifdef _KERNEL_OPT
     41 #include "lapic.h"
     42 #endif
     43 
     44 #include <sys/param.h>
     45 
     46 #ifdef _KERNEL
     47 #include <sys/systm.h>
     48 #include <x86/cpuvar.h>
     49 #include <x86/apicvar.h>
     50 #include <x86/cacheinfo.h>
     51 #include <machine/cpufunc.h>
     52 #include <machine/cputypes.h>
     53 #include <machine/specialreg.h>
     54 #else
     55 #include <stdarg.h>
     56 #include <stdbool.h>
     57 #include <stdio.h>
     58 #include <stdlib.h>
     59 #include <string.h>
     60 #include <x86/cacheinfo.h>
     61 #include <x86/cpuvar.h>
     62 #include "cpuctl.h"
     63 #include "cpuctl_i386.h"
     64 #endif
     65 
     66 #ifdef _KERNEL
     67 static uint64_t
     68 tsc_freq_vmware_cpuid(struct cpu_info *ci)
     69 {
     70 	uint32_t descs[4];
     71 	uint64_t freq;
     72 
     73 	if (cpu_max_hypervisor_cpuid < 0x40000010)
     74 		return 0;
     75 
     76 	x86_cpuid(0x40000010, descs);
     77 
     78 	freq = descs[0];
     79 	if (freq == 0)
     80 		return 0;
     81 
     82 	aprint_verbose(
     83 	    "got tsc frequency from vmware compatible cpuid\n");
     84 
     85 #if NLAPIC > 0
     86 	if (descs[1] > 0) {
     87 		aprint_verbose(
     88 		    "got lapic frequency from vmware compatible cpuid\n");
     89 		lapic_per_second = descs[1] * 1000;
     90 		lapic_from_cpuid = true;
     91 	}
     92 #endif
     93 
     94 	return freq * 1000;
     95 }
     96 #endif
     97 
     98 static uint64_t
     99 tsc_freq_cpuid(struct cpu_info *ci)
    100 {
    101 	uint64_t freq = 0, khz;
    102 	uint32_t descs[4];
    103 	uint32_t denominator, numerator;
    104 
    105 	if (ci->ci_max_cpuid < 0x15)
    106 		return 0;
    107 
    108 	x86_cpuid(0x15, descs);
    109 	denominator = descs[0];
    110 	numerator = descs[1];
    111 	if ((denominator != 0) && (numerator != 0)) {
    112 		khz = 0;
    113 		if (descs[2] != 0)
    114 			khz = descs[2] / 1000;
    115 		else if (CPUID_TO_FAMILY(ci->ci_signature) == 6) {
    116 			/*
    117 			 * Table 18-85 Nominal Core Crystal Clock Frequency,
    118 			 * 18.7.3 Determining the Processor Base Frequency,
    119 			 * Intel SDM.
    120 			 */
    121 			switch (CPUID_TO_MODEL(ci->ci_signature)) {
    122 			case 0x55: /* Xeon Scalable */
    123 			case 0x5f: /*
    124 				    * Atom Goldmont (Denverton). Right?
    125 				    * XXX Not documented!
    126 				    */
    127 				khz = 25000; /* 25.0 MHz */
    128 				break;
    129 			case 0x4e: /* 7th gen Core (Skylake) */
    130 			case 0x5e: /* 7th gen Core (Skylake) */
    131 			case 0x8e: /* 8th gen Core (Kaby Lake) */
    132 			case 0x9e: /* 8th gen Core (Kaby Lake) */
    133 				khz = 24000; /* 24.0 MHz */
    134 				break;
    135 			case 0x5c: /* Atom Goldmont */
    136 				khz = 19200; /* 19.2 MHz */
    137 				break;
    138 			default: /* Unknown */
    139 				break;
    140 			}
    141 		}
    142 		freq = khz * 1000 * numerator / denominator;
    143 		if (ci->ci_max_cpuid >= 0x16) {
    144 			x86_cpuid(0x16, descs);
    145 			if (descs[0] != 0) {
    146 				aprint_verbose_dev(ci->ci_dev,
    147 				    "CPU base freq %" PRIu64 " Hz\n",
    148 				    (uint64_t)descs[0] * 1000000);
    149 
    150 				/*
    151 				 * If we couldn't get frequency from
    152 				 * CPUID 0x15, use CPUID 0x16 EAX.
    153 				 */
    154 				if (freq == 0) {
    155 					khz = (uint64_t)descs[0] * 1000
    156 					    * denominator / numerator;
    157 					freq = (uint64_t)descs[0] * 1000000;
    158 				}
    159 			}
    160 			if (descs[1] != 0) {
    161 				aprint_verbose_dev(ci->ci_dev,
    162 				    "CPU max freq %" PRIu64 " Hz\n",
    163 				    (uint64_t)descs[1] * 1000000);
    164 			}
    165 		}
    166 #if defined(_KERNEL) &&  NLAPIC > 0
    167 		if ((khz != 0) && (lapic_per_second == 0)) {
    168 			lapic_per_second = khz * 1000;
    169 			aprint_debug_dev(ci->ci_dev,
    170 			    "lapic_per_second set to %" PRIu32 "\n",
    171 			    lapic_per_second);
    172 		}
    173 #endif
    174 	}
    175 	return freq;
    176 }
    177 
    178 uint64_t
    179 cpu_tsc_freq_cpuid(struct cpu_info *ci)
    180 {
    181 	uint64_t freq = 0;
    182 
    183 #ifdef _KERNEL
    184 	/* VMware compatible tsc and lapic frequency query */
    185 	if (vm_guest > VM_GUEST_NO)
    186 		freq = tsc_freq_vmware_cpuid(ci);
    187 #endif
    188 	if (freq == 0 && cpu_vendor == CPUVENDOR_INTEL)
    189 		freq = tsc_freq_cpuid(ci);
    190 	if (freq != 0)
    191 		aprint_verbose_dev(ci->ci_dev, "TSC freq CPUID %" PRIu64
    192 		    " Hz\n", freq);
    193 
    194 	return freq;
    195 }
    196 
    197 const struct x86_cache_info *
    198 cpu_cacheinfo_lookup(const struct x86_cache_info *cai, uint8_t desc)
    199 {
    200 	int i;
    201 
    202 	for (i = 0; cai[i].cai_desc != 0; i++) {
    203 		if (cai[i].cai_desc == desc)
    204 			return &cai[i];
    205 	}
    206 
    207 	return NULL;
    208 }
    209 
    210 /*
    211  * Get cache info from one of the following:
    212  *	Intel Deterministic Cache Parameter Leaf (0x04)
    213  *	AMD Cache Topology Information Leaf (0x8000001d)
    214  */
    215 void
    216 cpu_dcp_cacheinfo(struct cpu_info *ci, uint32_t leaf)
    217 {
    218 	u_int descs[4];
    219 	int type, level, ways, partitions, linesize, sets, totalsize;
    220 	int caitype = -1;
    221 	int i;
    222 
    223 	for (i = 0; ; i++) {
    224 		x86_cpuid2(leaf, i, descs);
    225 		type = __SHIFTOUT(descs[0], CPUID_DCP_CACHETYPE);
    226 		if (type == CPUID_DCP_CACHETYPE_N)
    227 			break;
    228 		level = __SHIFTOUT(descs[0], CPUID_DCP_CACHELEVEL);
    229 		switch (level) {
    230 		case 1:
    231 			if (type == CPUID_DCP_CACHETYPE_I)
    232 				caitype = CAI_ICACHE;
    233 			else if (type == CPUID_DCP_CACHETYPE_D)
    234 				caitype = CAI_DCACHE;
    235 			else
    236 				caitype = -1;
    237 			break;
    238 		case 2:
    239 			if (type == CPUID_DCP_CACHETYPE_U)
    240 				caitype = CAI_L2CACHE;
    241 			else
    242 				caitype = -1;
    243 			break;
    244 		case 3:
    245 			if (type == CPUID_DCP_CACHETYPE_U)
    246 				caitype = CAI_L3CACHE;
    247 			else
    248 				caitype = -1;
    249 			break;
    250 		default:
    251 			caitype = -1;
    252 			break;
    253 		}
    254 		if (caitype == -1)
    255 			continue;
    256 
    257 		ways = __SHIFTOUT(descs[1], CPUID_DCP_WAYS) + 1;
    258 		partitions =__SHIFTOUT(descs[1], CPUID_DCP_PARTITIONS)
    259 		    + 1;
    260 		linesize = __SHIFTOUT(descs[1], CPUID_DCP_LINESIZE)
    261 		    + 1;
    262 		sets = descs[2] + 1;
    263 		totalsize = ways * partitions * linesize * sets;
    264 		ci->ci_cinfo[caitype].cai_totalsize = totalsize;
    265 		ci->ci_cinfo[caitype].cai_associativity = ways;
    266 		ci->ci_cinfo[caitype].cai_linesize = linesize;
    267 	}
    268 }
    269