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