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