xref: /netbsd-src/sys/arch/x86/x86/identcpu_subr.c (revision fae021bceb7c2ef963adb57fa8fefdc4d6139146)
1 /* $NetBSD: identcpu_subr.c,v 1.9 2021/10/07 13:04:18 msaitoh 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.9 2021/10/07 13:04:18 msaitoh 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 "cpuctl.h"
62 #include "cpuctl_i386.h"
63 #endif
64 
65 uint64_t
cpu_tsc_freq_cpuid(struct cpu_info * ci)66 cpu_tsc_freq_cpuid(struct cpu_info *ci)
67 {
68 	uint64_t freq = 0, khz;
69 	uint32_t descs[4];
70 	uint32_t denominator, numerator;
71 
72 	if (!((ci->ci_max_cpuid >= 0x15) && (cpu_vendor == CPUVENDOR_INTEL)))
73 		return 0;
74 
75 	x86_cpuid(0x15, descs);
76 	denominator = descs[0];
77 	numerator = descs[1];
78 	if ((denominator != 0) && (numerator != 0)) {
79 		khz = 0;
80 		if (descs[2] != 0)
81 			khz = descs[2] / 1000;
82 		else if (CPUID_TO_FAMILY(ci->ci_signature) == 6) {
83 			/*
84 			 * Table 18-85 Nominal Core Crystal Clock Frequency,
85 			 * 18.7.3 Determining the Processor Base Frequency,
86 			 * Intel SDM.
87 			 */
88 			switch (CPUID_TO_MODEL(ci->ci_signature)) {
89 			case 0x55: /* Xeon Scalable */
90 			case 0x5f: /*
91 				    * Atom Goldmont (Denverton). Right?
92 				    * XXX Not documented!
93 				    */
94 				khz = 25000; /* 25.0 MHz */
95 				break;
96 			case 0x4e: /* 7th gen Core (Skylake) */
97 			case 0x5e: /* 7th gen Core (Skylake) */
98 			case 0x8e: /* 8th gen Core (Kaby Lake) */
99 			case 0x9e: /* 8th gen Core (Kaby Lake) */
100 				khz = 24000; /* 24.0 MHz */
101 				break;
102 			case 0x5c: /* Atom Goldmont */
103 				khz = 19200; /* 19.2 MHz */
104 				break;
105 			default: /* Unknown */
106 				break;
107 			}
108 		}
109 		freq = khz * 1000 * numerator / denominator;
110 		if (ci->ci_max_cpuid >= 0x16) {
111 			x86_cpuid(0x16, descs);
112 			if (descs[0] != 0) {
113 				aprint_verbose_dev(ci->ci_dev,
114 				    "CPU base freq %" PRIu64 " Hz\n",
115 				    (uint64_t)descs[0] * 1000000);
116 
117 				/*
118 				 * If we couldn't get frequency from
119 				 * CPUID 0x15, use CPUID 0x16 EAX.
120 				 */
121 				if (freq == 0) {
122 					khz = (uint64_t)descs[0] * 1000
123 					    * denominator / numerator;
124 					freq = (uint64_t)descs[0] * 1000000;
125 				}
126 			}
127 			if (descs[1] != 0) {
128 				aprint_verbose_dev(ci->ci_dev,
129 				    "CPU max freq %" PRIu64 " Hz\n",
130 				    (uint64_t)descs[1] * 1000000);
131 			}
132 		}
133 #if defined(_KERNEL) &&  NLAPIC > 0
134 		if ((khz != 0) && (lapic_per_second == 0)) {
135 			lapic_per_second = khz * 1000;
136 			aprint_debug_dev(ci->ci_dev,
137 			    "lapic_per_second set to %" PRIu32 "\n",
138 			    lapic_per_second);
139 		}
140 #endif
141 	}
142 	if (freq != 0)
143 		aprint_verbose_dev(ci->ci_dev, "TSC freq CPUID %" PRIu64
144 		    " Hz\n", freq);
145 
146 	return freq;
147 }
148 
149 const struct x86_cache_info *
cpu_cacheinfo_lookup(const struct x86_cache_info * cai,uint8_t desc)150 cpu_cacheinfo_lookup(const struct x86_cache_info *cai, uint8_t desc)
151 {
152 	int i;
153 
154 	for (i = 0; cai[i].cai_desc != 0; i++) {
155 		if (cai[i].cai_desc == desc)
156 			return &cai[i];
157 	}
158 
159 	return NULL;
160 }
161 
162 /*
163  * Get cache info from one of the following:
164  *	Intel Deterministic Cache Parameter Leaf (0x04)
165  *	AMD Cache Topology Information Leaf (0x8000001d)
166  */
167 void
cpu_dcp_cacheinfo(struct cpu_info * ci,uint32_t leaf)168 cpu_dcp_cacheinfo(struct cpu_info *ci, uint32_t leaf)
169 {
170 	u_int descs[4];
171 	int type, level, ways, partitions, linesize, sets, totalsize;
172 	int caitype = -1;
173 	int i;
174 
175 	for (i = 0; ; i++) {
176 		x86_cpuid2(leaf, i, descs);
177 		type = __SHIFTOUT(descs[0], CPUID_DCP_CACHETYPE);
178 		if (type == CPUID_DCP_CACHETYPE_N)
179 			break;
180 		level = __SHIFTOUT(descs[0], CPUID_DCP_CACHELEVEL);
181 		switch (level) {
182 		case 1:
183 			if (type == CPUID_DCP_CACHETYPE_I)
184 				caitype = CAI_ICACHE;
185 			else if (type == CPUID_DCP_CACHETYPE_D)
186 				caitype = CAI_DCACHE;
187 			else
188 				caitype = -1;
189 			break;
190 		case 2:
191 			if (type == CPUID_DCP_CACHETYPE_U)
192 				caitype = CAI_L2CACHE;
193 			else
194 				caitype = -1;
195 			break;
196 		case 3:
197 			if (type == CPUID_DCP_CACHETYPE_U)
198 				caitype = CAI_L3CACHE;
199 			else
200 				caitype = -1;
201 			break;
202 		default:
203 			caitype = -1;
204 			break;
205 		}
206 		if (caitype == -1)
207 			continue;
208 
209 		ways = __SHIFTOUT(descs[1], CPUID_DCP_WAYS) + 1;
210 		partitions =__SHIFTOUT(descs[1], CPUID_DCP_PARTITIONS)
211 		    + 1;
212 		linesize = __SHIFTOUT(descs[1], CPUID_DCP_LINESIZE)
213 		    + 1;
214 		sets = descs[2] + 1;
215 		totalsize = ways * partitions * linesize * sets;
216 		ci->ci_cinfo[caitype].cai_totalsize = totalsize;
217 		ci->ci_cinfo[caitype].cai_associativity = ways;
218 		ci->ci_cinfo[caitype].cai_linesize = linesize;
219 	}
220 }
221