xref: /openbsd-src/sys/arch/amd64/amd64/identcpu.c (revision e1973c5102583b8327761bd164427164129bbadc)
1*e1973c51Sdv /*	$OpenBSD: identcpu.c,v 1.148 2024/10/07 20:30:17 dv Exp $	*/
2f5df1827Smickey /*	$NetBSD: identcpu.c,v 1.1 2003/04/26 18:39:28 fvdl Exp $	*/
3f5df1827Smickey 
4f5df1827Smickey /*
5f5df1827Smickey  * Copyright (c) 2003 Wasabi Systems, Inc.
6f5df1827Smickey  * All rights reserved.
7f5df1827Smickey  *
8f5df1827Smickey  * Written by Frank van der Linden for Wasabi Systems, Inc.
9f5df1827Smickey  *
10f5df1827Smickey  * Redistribution and use in source and binary forms, with or without
11f5df1827Smickey  * modification, are permitted provided that the following conditions
12f5df1827Smickey  * are met:
13f5df1827Smickey  * 1. Redistributions of source code must retain the above copyright
14f5df1827Smickey  *    notice, this list of conditions and the following disclaimer.
15f5df1827Smickey  * 2. Redistributions in binary form must reproduce the above copyright
16f5df1827Smickey  *    notice, this list of conditions and the following disclaimer in the
17f5df1827Smickey  *    documentation and/or other materials provided with the distribution.
18f5df1827Smickey  * 3. All advertising materials mentioning features or use of this software
19f5df1827Smickey  *    must display the following acknowledgement:
20f5df1827Smickey  *      This product includes software developed for the NetBSD Project by
21f5df1827Smickey  *      Wasabi Systems, Inc.
22f5df1827Smickey  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
23f5df1827Smickey  *    or promote products derived from this software without specific prior
24f5df1827Smickey  *    written permission.
25f5df1827Smickey  *
26f5df1827Smickey  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
27f5df1827Smickey  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28f5df1827Smickey  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29f5df1827Smickey  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
30f5df1827Smickey  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31f5df1827Smickey  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32f5df1827Smickey  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33f5df1827Smickey  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34f5df1827Smickey  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35f5df1827Smickey  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36f5df1827Smickey  * POSSIBILITY OF SUCH DAMAGE.
37f5df1827Smickey  */
38f5df1827Smickey 
395922251fSderaadt #include <sys/param.h>
40f5df1827Smickey #include <sys/systm.h>
41b4cf4983Sclaudio #include <sys/atomic.h>
42b4cf4983Sclaudio #include <sys/proc.h>
435922251fSderaadt #include <sys/sysctl.h>
4477d6d4a2Smlarkin 
4577d6d4a2Smlarkin #include "vmm.h"
461d2c60d6Spatrick #include "pvbus.h"
4777d6d4a2Smlarkin 
48f5df1827Smickey #include <machine/cpu.h>
49f5df1827Smickey #include <machine/cpufunc.h>
50f5df1827Smickey 
511d2c60d6Spatrick #if NPVBUS > 0
521d2c60d6Spatrick #include <dev/pv/pvvar.h>
531d2c60d6Spatrick #endif
541d2c60d6Spatrick 
55ab8e1d10Sjsg void	replacesmap(void);
561fc8fad1Sguenther void	replacemeltdown(void);
57eb35b7b4Smikeb uint64_t cpu_freq(struct cpu_info *);
582db82850Skettenis void	tsc_identify(struct cpu_info *);
5949acae7eSmikeb void	tsc_timecounter_init(struct cpu_info *, uint64_t);
605fe96b9aSderaadt #if NVMM > 0
6177d6d4a2Smlarkin void	cpu_check_vmm_cap(struct cpu_info *);
625fe96b9aSderaadt #endif /* NVMM > 0 */
63ab8e1d10Sjsg 
64f5df1827Smickey /* sysctl wants this. */
65f5df1827Smickey char cpu_model[48];
665922251fSderaadt int cpuspeed;
67d1449161Sthib 
68f3e61100Smatthieu int amd64_has_xcrypt;
69ebaf145fSbluhm int amd64_pos_cbit;	/* C bit position for SEV */
7031816377Sjsg int has_rdrand;
71f63c0148Sjsg int has_rdseed;
72f5df1827Smickey 
735922251fSderaadt int
745922251fSderaadt cpu_amd64speed(int *freq)
755922251fSderaadt {
765922251fSderaadt 	*freq = cpuspeed;
775922251fSderaadt 	return (0);
785922251fSderaadt }
795922251fSderaadt 
800b390b5dStedu #ifndef SMALL_KERNEL
81b4cf4983Sclaudio void	intelcore_update_sensor(void *);
82b4cf4983Sclaudio void	cpu_hz_update_sensor(void *);
83b4cf4983Sclaudio 
840b390b5dStedu /*
850b390b5dStedu  * Temperature read on the CPU is relative to the maximum
860b390b5dStedu  * temperature supported by the CPU, Tj(Max).
877a17d890Sjsg  * Refer to:
887a17d890Sjsg  * 64-ia-32-architectures-software-developer-vol-3c-part-3-manual.pdf
897a17d890Sjsg  * Section 35 and
907a17d890Sjsg  * http://www.intel.com/content/dam/www/public/us/en/documents/
917a17d890Sjsg  * white-papers/cpu-monitoring-dts-peci-paper.pdf
927a17d890Sjsg  *
937a17d890Sjsg  * The temperature on Intel CPUs can be between 70 and 105 degC, since
947a17d890Sjsg  * Westmere we can read the TJmax from the die. For older CPUs we have
957a17d890Sjsg  * to guess or use undocumented MSRs. Then we subtract the temperature
967a17d890Sjsg  * portion of thermal status from max to get current temperature.
970b390b5dStedu  */
980b390b5dStedu void
990b390b5dStedu intelcore_update_sensor(void *args)
1000b390b5dStedu {
1010b390b5dStedu 	struct cpu_info *ci = (struct cpu_info *) args;
1020b390b5dStedu 	u_int64_t msr;
1030b390b5dStedu 	int max = 100;
1040b390b5dStedu 
1058c49ce85Skrw 	/* Only some Core family chips have MSR_TEMPERATURE_TARGET. */
1067a17d890Sjsg 	if (ci->ci_model == 0x0e &&
1077a17d890Sjsg 	    (rdmsr(MSR_TEMPERATURE_TARGET_UNDOCUMENTED) &
1087a17d890Sjsg 	     MSR_TEMPERATURE_TARGET_LOW_BIT_UNDOCUMENTED))
1090b390b5dStedu 		max = 85;
1100b390b5dStedu 
1117a17d890Sjsg 	/*
1127a17d890Sjsg 	 * Newer CPUs can tell you what their max temperature is.
1137a17d890Sjsg 	 * See: '64-ia-32-architectures-software-developer-
1147a17d890Sjsg 	 * vol-3c-part-3-manual.pdf'
1157a17d890Sjsg 	 */
1167a17d890Sjsg 	if (ci->ci_model > 0x17 && ci->ci_model != 0x1c &&
1177a17d890Sjsg 	    ci->ci_model != 0x26 && ci->ci_model != 0x27 &&
1187a17d890Sjsg 	    ci->ci_model != 0x35 && ci->ci_model != 0x36)
1197a17d890Sjsg 		max = MSR_TEMPERATURE_TARGET_TJMAX(
1207a17d890Sjsg 		    rdmsr(MSR_TEMPERATURE_TARGET));
1217a17d890Sjsg 
1220b390b5dStedu 	msr = rdmsr(MSR_THERM_STATUS);
1230b390b5dStedu 	if (msr & MSR_THERM_STATUS_VALID_BIT) {
1240b390b5dStedu 		ci->ci_sensor.value = max - MSR_THERM_STATUS_TEMP(msr);
125e1772967Stedu 		/* micro degrees */
1260b390b5dStedu 		ci->ci_sensor.value *= 1000000;
1270b390b5dStedu 		/* kelvin */
1280b390b5dStedu 		ci->ci_sensor.value += 273150000;
1290b390b5dStedu 		ci->ci_sensor.flags &= ~SENSOR_FINVALID;
1300b390b5dStedu 	} else {
1310b390b5dStedu 		ci->ci_sensor.value = 0;
1320b390b5dStedu 		ci->ci_sensor.flags |= SENSOR_FINVALID;
1330b390b5dStedu 	}
1340b390b5dStedu }
1350b390b5dStedu 
136b4cf4983Sclaudio /*
137b4cf4983Sclaudio  * Effective CPU frequency measurement
138b4cf4983Sclaudio  *
139b4cf4983Sclaudio  * Refer to:
140b4cf4983Sclaudio  *   64-ia-32-architectures-software-developer-vol-3b-part-2-manual.pdf
141b4cf4983Sclaudio  *   Section 14.2 and
142b4cf4983Sclaudio  *   OSRR for AMD Family 17h processors Section 2.1.2
143b4cf4983Sclaudio  * Round to 50Mhz which is the accuracy of this measurement.
144b4cf4983Sclaudio  */
145b4cf4983Sclaudio #define FREQ_50MHZ	(50ULL * 1000000ULL * 1000000ULL)
146b4cf4983Sclaudio void
147b4cf4983Sclaudio cpu_hz_update_sensor(void *args)
148b4cf4983Sclaudio {
149b4cf4983Sclaudio 	extern uint64_t	 tsc_frequency;
150b4cf4983Sclaudio 	struct cpu_info	*ci = args;
151b4cf4983Sclaudio 	uint64_t	 mperf, aperf, mdelta, adelta, val;
152b4cf4983Sclaudio 	unsigned long	 s;
153b4cf4983Sclaudio 
154b4cf4983Sclaudio 	sched_peg_curproc(ci);
155b4cf4983Sclaudio 
156b4cf4983Sclaudio 	s = intr_disable();
157b4cf4983Sclaudio 	mperf = rdmsr(MSR_MPERF);
158b4cf4983Sclaudio 	aperf = rdmsr(MSR_APERF);
159b4cf4983Sclaudio 	intr_restore(s);
160b4cf4983Sclaudio 
161b4cf4983Sclaudio 	mdelta = mperf - ci->ci_hz_mperf;
162b4cf4983Sclaudio 	adelta = aperf - ci->ci_hz_aperf;
163b4cf4983Sclaudio 	ci->ci_hz_mperf = mperf;
164b4cf4983Sclaudio 	ci->ci_hz_aperf = aperf;
165b4cf4983Sclaudio 
166b4cf4983Sclaudio 	if (mdelta > 0) {
167b4cf4983Sclaudio 		val = (adelta * 1000000) / mdelta * tsc_frequency;
168b4cf4983Sclaudio 		val = ((val + FREQ_50MHZ / 2) / FREQ_50MHZ) * FREQ_50MHZ;
169b4cf4983Sclaudio 		ci->ci_hz_sensor.value = val;
170b4cf4983Sclaudio 	}
171b4cf4983Sclaudio 
172cf31dfdeSmpi 	sched_unpeg_curproc();
173b4cf4983Sclaudio }
1740b390b5dStedu #endif
1750b390b5dStedu 
1765714d2eaSgwk void (*setperf_setup)(struct cpu_info *);
1775714d2eaSgwk 
178f3e61100Smatthieu void via_nano_setup(struct cpu_info *ci);
179f3e61100Smatthieu 
180ec99753cShaesbaert void cpu_topology(struct cpu_info *ci);
181ec99753cShaesbaert 
182f3e61100Smatthieu void
183f3e61100Smatthieu via_nano_setup(struct cpu_info *ci)
184f3e61100Smatthieu {
185f3e61100Smatthieu 	u_int32_t regs[4], val;
186f3e61100Smatthieu 	u_int64_t msreg;
187f3e61100Smatthieu 	int model = (ci->ci_signature >> 4) & 15;
188f3e61100Smatthieu 
189f3e61100Smatthieu 	if (model >= 9) {
190f3e61100Smatthieu 		CPUID(0xC0000000, regs[0], regs[1], regs[2], regs[3]);
191f3e61100Smatthieu 		val = regs[0];
192f3e61100Smatthieu 		if (val >= 0xC0000001) {
193f3e61100Smatthieu 			CPUID(0xC0000001, regs[0], regs[1], regs[2], regs[3]);
194f3e61100Smatthieu 			val = regs[3];
195f3e61100Smatthieu 		} else
196f3e61100Smatthieu 			val = 0;
197f3e61100Smatthieu 
198f3e61100Smatthieu 		if (val & (C3_CPUID_HAS_RNG | C3_CPUID_HAS_ACE))
199f3e61100Smatthieu 			printf("%s:", ci->ci_dev->dv_xname);
200f3e61100Smatthieu 
201f3e61100Smatthieu 		/* Enable RNG if present and disabled */
202f3e61100Smatthieu 		if (val & C3_CPUID_HAS_RNG) {
203f3e61100Smatthieu 			extern int viac3_rnd_present;
204f3e61100Smatthieu 
205f3e61100Smatthieu 			if (!(val & C3_CPUID_DO_RNG)) {
206f3e61100Smatthieu 				msreg = rdmsr(0x110B);
207f3e61100Smatthieu 				msreg |= 0x40;
208f3e61100Smatthieu 				wrmsr(0x110B, msreg);
209f3e61100Smatthieu 			}
210f3e61100Smatthieu 			viac3_rnd_present = 1;
211f3e61100Smatthieu 			printf(" RNG");
212f3e61100Smatthieu 		}
213f3e61100Smatthieu 
214f3e61100Smatthieu 		/* Enable AES engine if present and disabled */
215f3e61100Smatthieu 		if (val & C3_CPUID_HAS_ACE) {
216f3e61100Smatthieu #ifdef CRYPTO
217f3e61100Smatthieu 			if (!(val & C3_CPUID_DO_ACE)) {
218f3e61100Smatthieu 				msreg = rdmsr(0x1107);
219f3e61100Smatthieu 				msreg |= (0x01 << 28);
220f3e61100Smatthieu 				wrmsr(0x1107, msreg);
221f3e61100Smatthieu 			}
222f3e61100Smatthieu 			amd64_has_xcrypt |= C3_HAS_AES;
223f3e61100Smatthieu #endif /* CRYPTO */
224f3e61100Smatthieu 			printf(" AES");
225f3e61100Smatthieu 		}
226f3e61100Smatthieu 
227f3e61100Smatthieu 		/* Enable ACE2 engine if present and disabled */
228f3e61100Smatthieu 		if (val & C3_CPUID_HAS_ACE2) {
229f3e61100Smatthieu #ifdef CRYPTO
230f3e61100Smatthieu 			if (!(val & C3_CPUID_DO_ACE2)) {
231f3e61100Smatthieu 				msreg = rdmsr(0x1107);
232f3e61100Smatthieu 				msreg |= (0x01 << 28);
233f3e61100Smatthieu 				wrmsr(0x1107, msreg);
234f3e61100Smatthieu 			}
235f3e61100Smatthieu 			amd64_has_xcrypt |= C3_HAS_AESCTR;
236f3e61100Smatthieu #endif /* CRYPTO */
237f3e61100Smatthieu 			printf(" AES-CTR");
238f3e61100Smatthieu 		}
239f3e61100Smatthieu 
240f3e61100Smatthieu 		/* Enable SHA engine if present and disabled */
241f3e61100Smatthieu 		if (val & C3_CPUID_HAS_PHE) {
242f3e61100Smatthieu #ifdef CRYPTO
243f3e61100Smatthieu 			if (!(val & C3_CPUID_DO_PHE)) {
244f3e61100Smatthieu 				msreg = rdmsr(0x1107);
245f3e61100Smatthieu 				msreg |= (0x01 << 28/**/);
246f3e61100Smatthieu 				wrmsr(0x1107, msreg);
247f3e61100Smatthieu 			}
248f3e61100Smatthieu 			amd64_has_xcrypt |= C3_HAS_SHA;
249f3e61100Smatthieu #endif /* CRYPTO */
250f3e61100Smatthieu 			printf(" SHA1 SHA256");
251f3e61100Smatthieu 		}
252f3e61100Smatthieu 
253f3e61100Smatthieu 		/* Enable MM engine if present and disabled */
254f3e61100Smatthieu 		if (val & C3_CPUID_HAS_PMM) {
255f3e61100Smatthieu #ifdef CRYPTO
256f3e61100Smatthieu 			if (!(val & C3_CPUID_DO_PMM)) {
257f3e61100Smatthieu 				msreg = rdmsr(0x1107);
258f3e61100Smatthieu 				msreg |= (0x01 << 28/**/);
259f3e61100Smatthieu 				wrmsr(0x1107, msreg);
260f3e61100Smatthieu 			}
261f3e61100Smatthieu 			amd64_has_xcrypt |= C3_HAS_MM;
262f3e61100Smatthieu #endif /* CRYPTO */
263f3e61100Smatthieu 			printf(" RSA");
264f3e61100Smatthieu 		}
265f3e61100Smatthieu 
266f3e61100Smatthieu 		printf("\n");
267f3e61100Smatthieu 	}
268f3e61100Smatthieu }
269f3e61100Smatthieu 
270554ce152Skevlo #ifndef SMALL_KERNEL
271554ce152Skevlo void via_update_sensor(void *args);
272554ce152Skevlo void
273554ce152Skevlo via_update_sensor(void *args)
274554ce152Skevlo {
275554ce152Skevlo 	struct cpu_info *ci = (struct cpu_info *) args;
276554ce152Skevlo 	u_int64_t msr;
277554ce152Skevlo 
278554ce152Skevlo 	msr = rdmsr(MSR_CENT_TMTEMPERATURE);
279554ce152Skevlo 	ci->ci_sensor.value = (msr & 0xffffff);
280554ce152Skevlo 	/* micro degrees */
281554ce152Skevlo 	ci->ci_sensor.value *= 1000000;
282554ce152Skevlo 	ci->ci_sensor.value += 273150000;
283554ce152Skevlo 	ci->ci_sensor.flags &= ~SENSOR_FINVALID;
284554ce152Skevlo }
285554ce152Skevlo #endif
286554ce152Skevlo 
287eb35b7b4Smikeb uint64_t
2888998e210Sguenther cpu_freq_ctr(struct cpu_info *ci, uint32_t cpu_perf_eax,
2898998e210Sguenther     uint32_t cpu_perf_edx)
2908d742180Sdlg {
291eb35b7b4Smikeb 	uint64_t count, last_count, msr;
2928d742180Sdlg 
2938d742180Sdlg 	if ((ci->ci_flags & CPUF_CONST_TSC) == 0 ||
2948d742180Sdlg 	    (cpu_perf_eax & CPUIDEAX_VERID) <= 1 ||
2958d742180Sdlg 	    CPUIDEDX_NUM_FC(cpu_perf_edx) <= 1)
2968d742180Sdlg 		return (0);
2978d742180Sdlg 
2988d742180Sdlg 	msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
2998d742180Sdlg 	if (msr & MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK)) {
3008d742180Sdlg 		/* some hypervisor is dicking us around */
3018d742180Sdlg 		return (0);
3028d742180Sdlg 	}
3038d742180Sdlg 
3048d742180Sdlg 	msr |= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_1);
3058d742180Sdlg 	wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
3068d742180Sdlg 
3078d742180Sdlg 	msr = rdmsr(MSR_PERF_GLOBAL_CTRL) | MSR_PERF_GLOBAL_CTR1_EN;
3088d742180Sdlg 	wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
3098d742180Sdlg 
3108d742180Sdlg 	last_count = rdmsr(MSR_PERF_FIXED_CTR1);
3118d742180Sdlg 	delay(100000);
3128d742180Sdlg 	count = rdmsr(MSR_PERF_FIXED_CTR1);
3138d742180Sdlg 
3148d742180Sdlg 	msr = rdmsr(MSR_PERF_FIXED_CTR_CTRL);
3158d742180Sdlg 	msr &= MSR_PERF_FIXED_CTR_FC(1, MSR_PERF_FIXED_CTR_FC_MASK);
3168d742180Sdlg 	wrmsr(MSR_PERF_FIXED_CTR_CTRL, msr);
3178d742180Sdlg 
3188d742180Sdlg 	msr = rdmsr(MSR_PERF_GLOBAL_CTRL);
3198d742180Sdlg 	msr &= ~MSR_PERF_GLOBAL_CTR1_EN;
3208d742180Sdlg 	wrmsr(MSR_PERF_GLOBAL_CTRL, msr);
3218d742180Sdlg 
3228d742180Sdlg 	return ((count - last_count) * 10);
3238d742180Sdlg }
3248d742180Sdlg 
325eb35b7b4Smikeb uint64_t
326eb35b7b4Smikeb cpu_freq(struct cpu_info *ci)
3278d742180Sdlg {
328eb35b7b4Smikeb 	uint64_t last_count, count;
3299c3f850cSreyk 
3308d742180Sdlg 	last_count = rdtsc();
3318d742180Sdlg 	delay(100000);
3328d742180Sdlg 	count = rdtsc();
3338d742180Sdlg 
3348d742180Sdlg 	return ((count - last_count) * 10);
3358d742180Sdlg }
3368d742180Sdlg 
33773556a19Sguenther /* print flags from one cpuid for cpu0 */
33873556a19Sguenther static inline void
33973556a19Sguenther pcpu0id3(const char *id, char reg1, uint32_t val1, const char *bits1,
34073556a19Sguenther     char reg2, uint32_t val2, const char *bits2,
34173556a19Sguenther     char reg3, uint32_t val3, const char *bits3)
34273556a19Sguenther {
34373556a19Sguenther 	if (val1 || val2 || val3) {
34473556a19Sguenther 		printf("\ncpu0: cpuid %s", id);
34573556a19Sguenther 		if (val1)
34673556a19Sguenther 			printf(" e%cx=%b", reg1, val1, bits1);
34773556a19Sguenther 		if (val2)
34873556a19Sguenther 			printf(" e%cx=%b", reg2, val2, bits2);
34973556a19Sguenther 		if (val3)
35073556a19Sguenther 			printf(" e%cx=%b", reg3, val3, bits3);
35173556a19Sguenther 	}
35273556a19Sguenther }
35373556a19Sguenther 
35473556a19Sguenther /* print flags from one, 32-bit MSR for cpu0 */
35573556a19Sguenther static inline void
35673556a19Sguenther pmsr032(uint32_t msr, uint32_t value, const char *bits)
35773556a19Sguenther {
35873556a19Sguenther 	if (value)
35973556a19Sguenther 		printf("\ncpu0: msr %x=%b", msr, value, bits);
36073556a19Sguenther }
36173556a19Sguenther 
36273556a19Sguenther static void
36373556a19Sguenther pbitdiff(uint32_t value, uint32_t base_value, const char *bits)
36473556a19Sguenther {
36573556a19Sguenther 	uint32_t minus;
36673556a19Sguenther 	if (value == base_value)
36773556a19Sguenther 		return;
36873556a19Sguenther 	minus = base_value & ~value;
36973556a19Sguenther 	value &= ~base_value;
37073556a19Sguenther 	if (minus)
37173556a19Sguenther 		printf("-%b", minus, bits);
37273556a19Sguenther 	if (value)
37373556a19Sguenther 		printf("+%b", value, bits);
37473556a19Sguenther }
37573556a19Sguenther 
37673556a19Sguenther static inline void
37773556a19Sguenther pcpuid(struct cpu_info *ci, const char *id, char reg, uint32_t val,
37873556a19Sguenther     uint32_t prev_val, const char *bits)
37973556a19Sguenther {
38073556a19Sguenther 	if (CPU_IS_PRIMARY(ci))
38173556a19Sguenther 		pcpu0id3(id, reg, val, bits, 0, 0, NULL, 0, 0, NULL);
38273556a19Sguenther 	else if (val != prev_val) {
38373556a19Sguenther 		printf("\n%s: cpuid %s e%cx=", ci->ci_dev->dv_xname, id, reg);
38473556a19Sguenther 		pbitdiff(val, prev_val, bits);
38573556a19Sguenther 	}
38673556a19Sguenther }
38773556a19Sguenther 
38873556a19Sguenther static inline void
38973556a19Sguenther pcpuid2(struct cpu_info *ci, const char *id,
39073556a19Sguenther     char reg1, uint32_t val1, uint32_t prev_val1, const char *bits1,
39173556a19Sguenther     char reg2, uint32_t val2, uint32_t prev_val2, const char *bits2)
39273556a19Sguenther {
39373556a19Sguenther 	if (CPU_IS_PRIMARY(ci))
39473556a19Sguenther 		pcpu0id3(id, reg1, val1, bits1, reg2, val2, bits2, 0, 0,
39573556a19Sguenther 		    NULL);
39673556a19Sguenther 	else if (val1 != prev_val1 || val2 != prev_val2) {
39773556a19Sguenther 		printf("\n%s: cpuid %s", ci->ci_dev->dv_xname, id);
39873556a19Sguenther 		if (val1 != prev_val1) {
39973556a19Sguenther 			printf(" e%cx=", reg1);
40073556a19Sguenther 			pbitdiff(val1, prev_val1, bits1);
40173556a19Sguenther 		}
40273556a19Sguenther 		if (val2 != prev_val2) {
40373556a19Sguenther 			printf(" e%cx=", reg2);
40473556a19Sguenther 			pbitdiff(val2, prev_val2, bits2);
40573556a19Sguenther 		}
40673556a19Sguenther 	}
40773556a19Sguenther }
40873556a19Sguenther 
40973556a19Sguenther static inline void
41073556a19Sguenther pcpuid3(struct cpu_info *ci, const char *id,
41173556a19Sguenther     char reg1, uint32_t val1, uint32_t prev_val1, const char *bits1,
41273556a19Sguenther     char reg2, uint32_t val2, uint32_t prev_val2, const char *bits2,
41373556a19Sguenther     char reg3, uint32_t val3, uint32_t prev_val3, const char *bits3)
41473556a19Sguenther {
41573556a19Sguenther 	if (CPU_IS_PRIMARY(ci))
41673556a19Sguenther 		pcpu0id3(id, reg1, val1, bits1, reg2, val2, bits2, reg3, val3,
41773556a19Sguenther 		    bits3);
41873556a19Sguenther 	else if (val1 != prev_val1 || val2 != prev_val2 || val3 != prev_val3) {
41973556a19Sguenther 		printf("\n%s: cpuid %s", ci->ci_dev->dv_xname, id);
42073556a19Sguenther 		if (val1 != prev_val1) {
42173556a19Sguenther 			printf(" e%cx=", reg1);
42273556a19Sguenther 			pbitdiff(val1, prev_val1, bits1);
42373556a19Sguenther 		}
42473556a19Sguenther 		if (val2 != prev_val2) {
42573556a19Sguenther 			printf(" e%cx=", reg2);
42673556a19Sguenther 			pbitdiff(val2, prev_val2, bits2);
42773556a19Sguenther 		}
42873556a19Sguenther 		if (val3 != prev_val3) {
42973556a19Sguenther 			printf(" e%cx=", reg3);
43073556a19Sguenther 			pbitdiff(val3, prev_val3, bits3);
43173556a19Sguenther 		}
43273556a19Sguenther 	}
43373556a19Sguenther }
43473556a19Sguenther 
43573556a19Sguenther static inline void
43673556a19Sguenther pmsr32(struct cpu_info *ci, uint32_t msr, uint32_t value, uint32_t prev_value,
43773556a19Sguenther     const char *bits)
43873556a19Sguenther {
43973556a19Sguenther 	if (CPU_IS_PRIMARY(ci))
44073556a19Sguenther 		pmsr032(msr, value, bits);
44173556a19Sguenther 	else if (value != prev_value) {
44273556a19Sguenther 		printf("\n%s: msr %x=", ci->ci_dev->dv_xname, msr);
44373556a19Sguenther 		pbitdiff(value, prev_value, bits);
44473556a19Sguenther 	}
44573556a19Sguenther }
44673556a19Sguenther 
44773556a19Sguenther #ifdef MULTIPROCESSOR
44873556a19Sguenther static uint32_t prevcpu_perf_eax;
44973556a19Sguenther static uint32_t prevcpu_perf_edx;
45073556a19Sguenther #endif
45173556a19Sguenther 
45273556a19Sguenther static inline void
4538998e210Sguenther print_perf_cpuid(struct cpu_info *ci, uint32_t cpu_perf_eax,
4548998e210Sguenther     uint32_t cpu_perf_edx)
45573556a19Sguenther {
4568998e210Sguenther 	uint32_t version;
45773556a19Sguenther 
45873556a19Sguenther 	if (CPU_IS_PRIMARY(ci)) {
4598998e210Sguenther 		version = cpu_perf_eax & CPUIDEAX_VERID;
46073556a19Sguenther 		if (version == 0)
46173556a19Sguenther 			return;
46273556a19Sguenther 	}
46373556a19Sguenther #ifdef MULTIPROCESSOR
46473556a19Sguenther 	else {
46573556a19Sguenther 		/* if no difference on the bits we care about, say nothing */
4668998e210Sguenther 		if (((cpu_perf_eax ^ prevcpu_perf_eax) & 0x00ffffff) == 0 &&
4678998e210Sguenther 		    ((cpu_perf_edx ^ prevcpu_perf_edx) & 0x00001fff) == 0)
46873556a19Sguenther 			return;
4698998e210Sguenther 		version = cpu_perf_eax & CPUIDEAX_VERID;
47073556a19Sguenther 	}
4718998e210Sguenther 	prevcpu_perf_eax = cpu_perf_eax;
4728998e210Sguenther 	prevcpu_perf_edx = cpu_perf_edx;
47373556a19Sguenther #endif
47473556a19Sguenther 
47573556a19Sguenther 	printf("\n%s: cpuid a vers=%d", ci->ci_dev->dv_xname, version);
47673556a19Sguenther 	if (version) {
4778998e210Sguenther 		printf(", gp=%d, gpwidth=%d", CPUIDEAX_NUM_GC(cpu_perf_eax),
4788998e210Sguenther 		    CPUIDEAX_BIT_GC(cpu_perf_eax));
47973556a19Sguenther 		if (version > 1) {
4808998e210Sguenther 			printf(", ff=%d, ffwidth=%d",
4818998e210Sguenther 			    CPUIDEDX_NUM_FC(cpu_perf_edx),
4828998e210Sguenther 			    CPUIDEDX_BIT_FC(cpu_perf_edx));
48373556a19Sguenther 		}
48473556a19Sguenther 	}
48573556a19Sguenther }
48673556a19Sguenther 
487f5df1827Smickey void
488f5df1827Smickey identifycpu(struct cpu_info *ci)
489f5df1827Smickey {
49073556a19Sguenther 	static uint32_t prevcpu_1_ecx, prevcpu_tpm_ecxflags, prevcpu_d_1_eax;
49173556a19Sguenther 	static uint32_t prevcpu_apmi_edx, prevcpu_arch_capa;
49273556a19Sguenther 	static struct cpu_info *prevci = &cpu_info_primary;
49373556a19Sguenther #define CPUID_MEMBER(member)	ci->member, prevci->member
49473556a19Sguenther 	uint32_t cflushsz, curcpu_1_ecx, curcpu_apmi_edx = 0;
4958998e210Sguenther 	uint32_t curcpu_perf_eax = 0, curcpu_perf_edx = 0;
49673556a19Sguenther 	uint32_t curcpu_tpm_ecxflags = 0, curcpu_d_1_eax = 0;
497eb35b7b4Smikeb 	uint64_t freq = 0;
49873556a19Sguenther 	u_int32_t dummy;
4996c090d7fShaesbaert 	char mycpu_model[48];
5002c60510eSgrange 	char *brandstr_from, *brandstr_to;
5012c60510eSgrange 	int skipspace;
502f5df1827Smickey 
50377d6d4a2Smlarkin 	CPUID(0x80000000, ci->ci_pnfeatset, dummy, dummy, dummy);
50473556a19Sguenther 	CPUID(0x80000001, ci->ci_efeature_eax, dummy, ci->ci_efeature_ecx,
50573556a19Sguenther 	    ci->ci_feature_eflags);
50673556a19Sguenther 
50773556a19Sguenther 	if (CPU_IS_PRIMARY(ci)) {
50873556a19Sguenther 		ci->ci_signature = cpu_id;
50973556a19Sguenther 		ci->ci_feature_flags = cpu_feature & ~CPUID_NXE;
51073556a19Sguenther 		cflushsz = cpu_ebxfeature;
51173556a19Sguenther 		curcpu_1_ecx = cpu_ecxfeature;
51277d6d4a2Smlarkin 		ecpu_ecxfeature = ci->ci_efeature_ecx;
51373556a19Sguenther 	} else {
51473556a19Sguenther 		CPUID(1, ci->ci_signature, cflushsz, curcpu_1_ecx,
51573556a19Sguenther 		    ci->ci_feature_flags);
51629636fcfSmlarkin 		/* Let cpu_feature be the common bits */
51773556a19Sguenther 		cpu_feature &= ci->ci_feature_flags |
51873556a19Sguenther 		    (ci->ci_feature_eflags & CPUID_NXE);
519a14dd3ebSguenther 		cpu_ecxfeature &= curcpu_1_ecx;
52073556a19Sguenther 	}
52173556a19Sguenther 	/* cflush cacheline size is equal to bits 15-8 of ebx * 8 */
52273556a19Sguenther 	ci->ci_cflushsz = ((cflushsz >> 8) & 0xff) * 8;
5236c090d7fShaesbaert 
52477d6d4a2Smlarkin 	CPUID(0x80000002, ci->ci_brand[0],
52577d6d4a2Smlarkin 	    ci->ci_brand[1], ci->ci_brand[2], ci->ci_brand[3]);
52677d6d4a2Smlarkin 	CPUID(0x80000003, ci->ci_brand[4],
52777d6d4a2Smlarkin 	    ci->ci_brand[5], ci->ci_brand[6], ci->ci_brand[7]);
52877d6d4a2Smlarkin 	CPUID(0x80000004, ci->ci_brand[8],
52977d6d4a2Smlarkin 	    ci->ci_brand[9], ci->ci_brand[10], ci->ci_brand[11]);
53077d6d4a2Smlarkin 	strlcpy(mycpu_model, (char *)ci->ci_brand, sizeof(mycpu_model));
531f5df1827Smickey 
532def6b364Skettenis 	/* Remove leading, trailing and duplicated spaces from mycpu_model */
5336c090d7fShaesbaert 	brandstr_from = brandstr_to = mycpu_model;
5342c60510eSgrange 	skipspace = 1;
5352c60510eSgrange 	while (*brandstr_from != '\0') {
5362c60510eSgrange 		if (!skipspace || *brandstr_from != ' ') {
5372c60510eSgrange 			skipspace = 0;
5382c60510eSgrange 			*(brandstr_to++) = *brandstr_from;
5392c60510eSgrange 		}
5402c60510eSgrange 		if (*brandstr_from == ' ')
5412c60510eSgrange 			skipspace = 1;
5422c60510eSgrange 		brandstr_from++;
5432c60510eSgrange 	}
544def6b364Skettenis 	if (skipspace && brandstr_to > mycpu_model)
545def6b364Skettenis 		brandstr_to--;
5462c60510eSgrange 	*brandstr_to = '\0';
5472c60510eSgrange 
5486c090d7fShaesbaert 	if (mycpu_model[0] == 0)
5496c090d7fShaesbaert 		strlcpy(mycpu_model, "Opteron or Athlon 64",
5506c090d7fShaesbaert 		    sizeof(mycpu_model));
5516c090d7fShaesbaert 
5526c090d7fShaesbaert 	/* If primary cpu, fill in the global cpu_model used by sysctl */
553dca1e8dbSfcambus 	if (CPU_IS_PRIMARY(ci))
5546c090d7fShaesbaert 		strlcpy(cpu_model, mycpu_model, sizeof(cpu_model));
555f5df1827Smickey 
55640d90450Sjsg 	ci->ci_family = (ci->ci_signature >> 8) & 0x0f;
55740d90450Sjsg 	ci->ci_model = (ci->ci_signature >> 4) & 0x0f;
55840d90450Sjsg 	if (ci->ci_family == 0x6 || ci->ci_family == 0xf) {
55940d90450Sjsg 		ci->ci_family += (ci->ci_signature >> 20) & 0xff;
56040d90450Sjsg 		ci->ci_model += ((ci->ci_signature >> 16) & 0x0f) << 4;
56140d90450Sjsg 	}
56240d90450Sjsg 
5631d2c60d6Spatrick #if NPVBUS > 0
5641d2c60d6Spatrick 	/* Detect hypervisors early, attach the paravirtual bus later */
5651d2c60d6Spatrick 	if (CPU_IS_PRIMARY(ci) && cpu_ecxfeature & CPUIDECX_HV)
5661d2c60d6Spatrick 		pvbus_identify();
5671d2c60d6Spatrick #endif
5681d2c60d6Spatrick 
5698998e210Sguenther 	if (ci->ci_pnfeatset >= 0x80000007)
5708998e210Sguenther 		CPUID(0x80000007, dummy, dummy, dummy, curcpu_apmi_edx);
5718998e210Sguenther 
57207166672Smglocker 	if (ci->ci_feature_flags && ci->ci_feature_flags & CPUID_TSC) {
57307166672Smglocker 		/* Has TSC, check if it's constant */
574ccd74f94Sguenther 		if (ci->ci_vendor == CPUV_INTEL) {
57507166672Smglocker 			if ((ci->ci_family == 0x0f && ci->ci_model >= 0x03) ||
57607166672Smglocker 			    (ci->ci_family == 0x06 && ci->ci_model >= 0x0e)) {
5770403d5bcSguenther 				atomic_setbits_int(&ci->ci_flags, CPUF_CONST_TSC);
57807166672Smglocker 			}
579ccd74f94Sguenther 		} else if (ci->ci_vendor == CPUV_VIA) {
58007166672Smglocker 			/* VIA */
58107166672Smglocker 			if (ci->ci_model >= 0x0f) {
5820403d5bcSguenther 				atomic_setbits_int(&ci->ci_flags, CPUF_CONST_TSC);
58307166672Smglocker 			}
584ccd74f94Sguenther 		} else if (ci->ci_vendor == CPUV_AMD) {
5858998e210Sguenther 			if (curcpu_apmi_edx & CPUIDEDX_ITSC) {
5860403d5bcSguenther 				/* Invariant TSC indicates constant TSC on AMD */
5870403d5bcSguenther 				atomic_setbits_int(&ci->ci_flags, CPUF_CONST_TSC);
58807166672Smglocker 			}
58907166672Smglocker 		}
5909c3f850cSreyk 
5919c3f850cSreyk 		/* Check if it's an invariant TSC */
5928998e210Sguenther 		if (curcpu_apmi_edx & CPUIDEDX_ITSC)
5930403d5bcSguenther 			atomic_setbits_int(&ci->ci_flags, CPUF_INVAR_TSC);
5942db82850Skettenis 
5952db82850Skettenis 		tsc_identify(ci);
59607166672Smglocker 	}
59707166672Smglocker 
5988998e210Sguenther 	if (ci->ci_cpuid_level >= 0xa) {
5998998e210Sguenther 		CPUID(0xa, curcpu_perf_eax, dummy, dummy, curcpu_perf_edx);
6008998e210Sguenther 
6018998e210Sguenther 		freq = cpu_freq_ctr(ci, curcpu_perf_eax, curcpu_perf_edx);
6028998e210Sguenther 	}
6038998e210Sguenther 	if (freq == 0)
604eb35b7b4Smikeb 		freq = cpu_freq(ci);
605f5df1827Smickey 
60673556a19Sguenther 	if (ci->ci_cpuid_level >= 0x07) {
60773556a19Sguenther 		/* "Structured Extended Feature Flags" */
60873556a19Sguenther 		CPUID_LEAF(0x7, 0, dummy, ci->ci_feature_sefflags_ebx,
60973556a19Sguenther 		    ci->ci_feature_sefflags_ecx, ci->ci_feature_sefflags_edx);
61073556a19Sguenther 		/* SEFF0ECX_OSPKE is set late on AP */
61173556a19Sguenther 		ci->ci_feature_sefflags_ecx &= ~SEFF0ECX_OSPKE;
61273556a19Sguenther 	}
61373556a19Sguenther 
6146c090d7fShaesbaert 	printf("%s: %s", ci->ci_dev->dv_xname, mycpu_model);
615f5df1827Smickey 
616eb35b7b4Smikeb 	if (freq != 0)
617eb35b7b4Smikeb 		printf(", %llu.%02llu MHz", (freq + 4999) / 1000000,
618eb35b7b4Smikeb 		    ((freq + 4999) / 10000) % 100);
6196c090d7fShaesbaert 
620dca1e8dbSfcambus 	if (CPU_IS_PRIMARY(ci)) {
621eb35b7b4Smikeb 		cpuspeed = (freq + 4999) / 1000000;
6225922251fSderaadt 		cpu_cpuspeed = cpu_amd64speed;
6236c090d7fShaesbaert 	}
624f5df1827Smickey 
62555992104Sjsg 	printf(", %02x-%02x-%02x", ci->ci_family, ci->ci_model,
62655992104Sjsg 	    ci->ci_signature & 0x0f);
62755992104Sjsg 
628f5923053Sjsg 	if ((cpu_ecxfeature & CPUIDECX_HV) == 0) {
629f5923053Sjsg 		uint64_t level = 0;
630f5923053Sjsg 		uint32_t dummy;
631f5923053Sjsg 
632ccd74f94Sguenther 		if (ci->ci_vendor == CPUV_AMD) {
633f5923053Sjsg 			level = rdmsr(MSR_PATCH_LEVEL);
634ccd74f94Sguenther 		} else if (ci->ci_vendor == CPUV_INTEL) {
635f5923053Sjsg 			wrmsr(MSR_BIOS_SIGN, 0);
636f5923053Sjsg 			CPUID(1, dummy, dummy, dummy, dummy);
637f5923053Sjsg 			level = rdmsr(MSR_BIOS_SIGN) >> 32;
638f5923053Sjsg 		}
639f5923053Sjsg 		if (level != 0)
640f5923053Sjsg 			printf(", patch %08llx", level);
641f5923053Sjsg 	}
642f5923053Sjsg 
64373556a19Sguenther 	if (ci->ci_cpuid_level >= 0x06)
64473556a19Sguenther 		CPUID(0x06, ci->ci_feature_tpmflags, dummy,
64573556a19Sguenther 		    curcpu_tpm_ecxflags, dummy);
64673556a19Sguenther 	if (ci->ci_vendor == CPUV_AMD && ci->ci_family >= 0x12)
64783ef434fSkettenis 		ci->ci_feature_tpmflags |= TPM_ARAT;
64873556a19Sguenther 
64973556a19Sguenther 	/* xsave subfeatures */
65073556a19Sguenther 	if (ci->ci_cpuid_level >= 0xd)
65173556a19Sguenther 		CPUID_LEAF(0xd, 1, curcpu_d_1_eax, dummy, dummy, dummy);
65273556a19Sguenther 
65373556a19Sguenther 	pcpuid2(ci, "1", 'd', CPUID_MEMBER(ci_feature_flags), CPUID_EDX_BITS,
65473556a19Sguenther 	    'c', curcpu_1_ecx, prevcpu_1_ecx, CPUID_ECX_BITS);
65573556a19Sguenther 	pcpuid2(ci, "6", 'a', CPUID_MEMBER(ci_feature_tpmflags), TPM_EAX_BITS,
65673556a19Sguenther 	    'c', curcpu_tpm_ecxflags, prevcpu_tpm_ecxflags, TPM_ECX_BITS);
65773556a19Sguenther 	pcpuid3(ci, "7.0",
65873556a19Sguenther 	    'b', CPUID_MEMBER(ci_feature_sefflags_ebx), SEFF0_EBX_BITS,
65973556a19Sguenther 	    'c', CPUID_MEMBER(ci_feature_sefflags_ecx), SEFF0_ECX_BITS,
66073556a19Sguenther 	    'd', CPUID_MEMBER(ci_feature_sefflags_edx), SEFF0_EDX_BITS);
6618998e210Sguenther 	print_perf_cpuid(ci, curcpu_perf_eax, curcpu_perf_edx);
66273556a19Sguenther 	pcpuid(ci, "d.1", 'a', curcpu_d_1_eax, prevcpu_d_1_eax, XSAVE_BITS);
66373556a19Sguenther 	pcpuid2(ci, "80000001",
66473556a19Sguenther 	    'd', CPUID_MEMBER(ci_feature_eflags), CPUIDE_EDX_BITS,
66573556a19Sguenther 	    'c', CPUID_MEMBER(ci_efeature_ecx), CPUIDE_ECX_BITS);
66673556a19Sguenther 	pcpuid(ci, "80000007", 'd', curcpu_apmi_edx, prevcpu_apmi_edx,
66773556a19Sguenther 	    CPUID_APMI_EDX_BITS);
66873556a19Sguenther #ifdef MULTIPROCESSOR
66973556a19Sguenther 	prevcpu_1_ecx = curcpu_1_ecx;
67073556a19Sguenther 	prevcpu_tpm_ecxflags = curcpu_tpm_ecxflags;
67173556a19Sguenther 	prevcpu_d_1_eax = curcpu_d_1_eax;
67273556a19Sguenther 	prevcpu_apmi_edx = curcpu_apmi_edx;
67373556a19Sguenther #endif
674cf77ca15Sguenther 
675b70fbf0aSguenther 	/* speculation control features */
676ccd74f94Sguenther 	if (ci->ci_vendor == CPUV_AMD) {
677d9b434b4Smlarkin 		if (ci->ci_pnfeatset >= 0x80000008) {
678d9b434b4Smlarkin 			CPUID(0x80000008, dummy, ci->ci_feature_amdspec_ebx,
679d9b434b4Smlarkin 			    dummy, dummy);
68073556a19Sguenther 			pcpuid(ci, "80000008", 'b',
68173556a19Sguenther 			    CPUID_MEMBER(ci_feature_amdspec_ebx),
68273556a19Sguenther 			    CPUID_AMDSPEC_EBX_BITS);
683d9b434b4Smlarkin 		}
68473556a19Sguenther 	} else if (ci->ci_vendor == CPUV_INTEL) {
68573556a19Sguenther 		if (ci->ci_feature_sefflags_edx & SEFF0EDX_ARCH_CAP) {
68673556a19Sguenther 			uint32_t msr = rdmsr(MSR_ARCH_CAPABILITIES);
687b70fbf0aSguenther 
68873556a19Sguenther 			pmsr32(ci, MSR_ARCH_CAPABILITIES, msr,
68973556a19Sguenther 			    prevcpu_arch_capa, ARCH_CAP_MSR_BITS);
69073556a19Sguenther 			prevcpu_arch_capa = msr;
69173556a19Sguenther 			if (!CPU_IS_PRIMARY(ci) && cpu_meltdown &&
69273556a19Sguenther 			    (msr & ARCH_CAP_RDCL_NO))
69373556a19Sguenther 				printf("\n%s: -MELTDOWN", ci->ci_dev->dv_xname);
694d9b434b4Smlarkin 		}
69573556a19Sguenther 		if (cpu_meltdown && CPU_IS_PRIMARY(ci))
69673556a19Sguenther 			printf("\n%s: MELTDOWN", ci->ci_dev->dv_xname);
6974c13963cSguenther 	}
6984c13963cSguenther 
699f880dfafSbluhm 	/* AMD secure memory encryption and encrypted virtualization features */
700f880dfafSbluhm 	if (ci->ci_vendor == CPUV_AMD &&
701f880dfafSbluhm 	    ci->ci_pnfeatset >= CPUID_AMD_SEV_CAP) {
702f880dfafSbluhm 		CPUID(CPUID_AMD_SEV_CAP, ci->ci_feature_amdsev_eax,
703f880dfafSbluhm 		    ci->ci_feature_amdsev_ebx, ci->ci_feature_amdsev_ecx,
704f880dfafSbluhm 		    ci->ci_feature_amdsev_edx);
705f880dfafSbluhm 		pcpuid3(ci, "8000001F",
706f880dfafSbluhm 		    'a', CPUID_MEMBER(ci_feature_amdsev_eax),
707f880dfafSbluhm 		    CPUID_AMDSEV_EAX_BITS,
708f880dfafSbluhm 		    'c', CPUID_MEMBER(ci_feature_amdsev_ecx),
709f880dfafSbluhm 		    CPUID_AMDSEV_ECX_BITS,
710f880dfafSbluhm 		    'd', CPUID_MEMBER(ci_feature_amdsev_edx),
711f880dfafSbluhm 		    CPUID_AMDSEV_EDX_BITS);
712f880dfafSbluhm 		amd64_pos_cbit = (ci->ci_feature_amdsev_ebx & 0x3f);
713f880dfafSbluhm 	}
714f880dfafSbluhm 
7154aac7a55Smickey 	printf("\n");
716f5df1827Smickey 
717f95e373fSguenther 	replacemeltdown();
718f5df1827Smickey 	x86_print_cacheinfo(ci);
71934b4ab5eSuwe 
720dca1e8dbSfcambus 	if (CPU_IS_PRIMARY(ci)) {
721c61a50a0Sderaadt #ifndef SMALL_KERNEL
722ccd74f94Sguenther 		if (ci->ci_vendor == CPUV_AMD &&
72377d6d4a2Smlarkin 		    ci->ci_pnfeatset >= 0x80000007) {
72473556a19Sguenther 			if (curcpu_apmi_edx & 0x06) {
725cba131fcSmlarkin 				if ((ci->ci_signature & 0xF00) == 0xF00)
7265714d2eaSgwk 					setperf_setup = k8_powernow_init;
72734b4ab5eSuwe 			}
728fb8a9091Sderaadt 			if (ci->ci_family >= 0x10)
7290df720aaSclaudio 				setperf_setup = k1x_init;
73034b4ab5eSuwe 		}
731d279ab14Stom 
7326c090d7fShaesbaert 		if (cpu_ecxfeature & CPUIDECX_EST)
7330b390b5dStedu 			setperf_setup = est_init;
734c61a50a0Sderaadt #endif
73504bf4ebbSgwk 
73631816377Sjsg 		if (cpu_ecxfeature & CPUIDECX_RDRAND)
73731816377Sjsg 			has_rdrand = 1;
738ab8e1d10Sjsg 
739f63c0148Sjsg 		if (ci->ci_feature_sefflags_ebx & SEFF0EBX_RDSEED)
740f63c0148Sjsg 			has_rdseed = 1;
741f63c0148Sjsg 
74277d6d4a2Smlarkin 		if (ci->ci_feature_sefflags_ebx & SEFF0EBX_SMAP)
743ab8e1d10Sjsg 			replacesmap();
7446c090d7fShaesbaert 	}
745c8cdbfc9Sjsg 
746c8cdbfc9Sjsg #ifndef SMALL_KERNEL
747b7f63170Skn 	if (CPU_IS_PRIMARY(ci) && (ci->ci_feature_tpmflags & TPM_SENSOR) &&
748b7f63170Skn 	    ci->ci_vendor == CPUV_INTEL) {
7490b390b5dStedu 		ci->ci_sensor.type = SENSOR_TEMP;
7500b390b5dStedu 		sensor_task_register(ci, intelcore_update_sensor, 5);
7510b390b5dStedu 		sensor_attach(&ci->ci_sensordev, &ci->ci_sensor);
7520b390b5dStedu 	}
7537e5f337fStb #endif
7540b390b5dStedu 
755ccd74f94Sguenther 	if (CPU_IS_PRIMARY(ci) && ci->ci_vendor == CPUV_VIA) {
756f3e61100Smatthieu 		ci->cpu_setup = via_nano_setup;
757554ce152Skevlo #ifndef SMALL_KERNEL
758554ce152Skevlo 		ci->ci_sensor.type = SENSOR_TEMP;
759554ce152Skevlo 		sensor_task_register(ci, via_update_sensor, 5);
760554ce152Skevlo 		sensor_attach(&ci->ci_sensordev, &ci->ci_sensor);
761554ce152Skevlo #endif
762554ce152Skevlo 	}
763ec99753cShaesbaert 
76449acae7eSmikeb 	tsc_timecounter_init(ci, freq);
7659c3f850cSreyk 
766ec99753cShaesbaert 	cpu_topology(ci);
7675fe96b9aSderaadt #if NVMM > 0
76877d6d4a2Smlarkin 	cpu_check_vmm_cap(ci);
7695fe96b9aSderaadt #endif /* NVMM > 0 */
770b4cf4983Sclaudio 
771b4cf4983Sclaudio 	/* Check for effective frequency via MPERF, APERF */
77273556a19Sguenther 	if ((curcpu_tpm_ecxflags & TPM_EFFFREQ) && ci->ci_smt_id == 0) {
773b4cf4983Sclaudio #ifndef SMALL_KERNEL
774b4cf4983Sclaudio 		ci->ci_hz_sensor.type = SENSOR_FREQ;
775b4cf4983Sclaudio 		sensor_task_register(ci, cpu_hz_update_sensor, 1);
776b4cf4983Sclaudio 		sensor_attach(&ci->ci_sensordev, &ci->ci_hz_sensor);
777b4cf4983Sclaudio #endif
778b4cf4983Sclaudio 	}
77973556a19Sguenther 	prevci = ci;
780ec99753cShaesbaert }
781ec99753cShaesbaert 
782ec99753cShaesbaert #ifndef SMALL_KERNEL
783ec99753cShaesbaert /*
784ec99753cShaesbaert  * Base 2 logarithm of an int. returns 0 for 0 (yeye, I know).
785ec99753cShaesbaert  */
786ec99753cShaesbaert static int
787ec99753cShaesbaert log2(unsigned int i)
788ec99753cShaesbaert {
789ec99753cShaesbaert 	int ret = 0;
790ec99753cShaesbaert 
791ec99753cShaesbaert 	while (i >>= 1)
792ec99753cShaesbaert 		ret++;
793ec99753cShaesbaert 
794ec99753cShaesbaert 	return (ret);
795ec99753cShaesbaert }
796ec99753cShaesbaert 
797ec99753cShaesbaert static int
798ec99753cShaesbaert mask_width(u_int x)
799ec99753cShaesbaert {
800ec99753cShaesbaert 	int bit;
801ec99753cShaesbaert 	int mask;
802ec99753cShaesbaert 	int powerof2;
803ec99753cShaesbaert 
804ec99753cShaesbaert 	powerof2 = ((x - 1) & x) == 0;
805ec99753cShaesbaert 	mask = (x << (1 - powerof2)) - 1;
806ec99753cShaesbaert 
807ec99753cShaesbaert 	/* fls */
808ec99753cShaesbaert 	if (mask == 0)
809ec99753cShaesbaert 		return (0);
810ec99753cShaesbaert 	for (bit = 1; mask != 1; bit++)
811ec99753cShaesbaert 		mask = (unsigned int)mask >> 1;
812ec99753cShaesbaert 
813ec99753cShaesbaert 	return (bit);
814ec99753cShaesbaert }
815ec99753cShaesbaert #endif
816ec99753cShaesbaert 
817ec99753cShaesbaert /*
818ec99753cShaesbaert  * Build up cpu topology for given cpu, must run on the core itself.
819ec99753cShaesbaert  */
820ec99753cShaesbaert void
821ec99753cShaesbaert cpu_topology(struct cpu_info *ci)
822ec99753cShaesbaert {
823ec99753cShaesbaert #ifndef SMALL_KERNEL
824ec99753cShaesbaert 	u_int32_t eax, ebx, ecx, edx;
82552f20651Smlarkin 	u_int32_t apicid, max_apicid = 0, max_coreid = 0;
82652f20651Smlarkin 	u_int32_t smt_bits = 0, core_bits, pkg_bits = 0;
82752f20651Smlarkin 	u_int32_t smt_mask = 0, core_mask, pkg_mask = 0;
828ec99753cShaesbaert 
829ec99753cShaesbaert 	/* We need at least apicid at CPUID 1 */
830ccd74f94Sguenther 	if (ci->ci_cpuid_level < 1)
831ec99753cShaesbaert 		goto no_topology;
832ec99753cShaesbaert 
833ec99753cShaesbaert 	/* Initial apicid */
834ec99753cShaesbaert 	CPUID(1, eax, ebx, ecx, edx);
835ec99753cShaesbaert 	apicid = (ebx >> 24) & 0xff;
836ec99753cShaesbaert 
837ccd74f94Sguenther 	if (ci->ci_vendor == CPUV_AMD) {
838fccfca3aSdlg 		uint32_t nthreads = 1; /* per core */
839fccfca3aSdlg 		uint32_t thread_id; /* within a package */
840fccfca3aSdlg 
841ec99753cShaesbaert 		/* We need at least apicid at CPUID 0x80000008 */
8427c3cbf8aSguenther 		if (ci->ci_pnfeatset < 0x80000008)
843ec99753cShaesbaert 			goto no_topology;
844ec99753cShaesbaert 
845ec99753cShaesbaert 		CPUID(0x80000008, eax, ebx, ecx, edx);
846ec99753cShaesbaert 		core_bits = (ecx >> 12) & 0xf;
847fccfca3aSdlg 
848fccfca3aSdlg 		if (ci->ci_pnfeatset >= 0x8000001e) {
849fccfca3aSdlg 			CPUID(0x8000001e, eax, ebx, ecx, edx);
850fccfca3aSdlg 			nthreads = ((ebx >> 8) & 0xf) + 1;
851121fa492Ssthen 		}
852fccfca3aSdlg 
853fccfca3aSdlg 		/* Shift the core_bits off to get at the pkg bits */
854fccfca3aSdlg 		ci->ci_pkg_id = apicid >> core_bits;
855fccfca3aSdlg 
856fccfca3aSdlg 		/* Get rid of the package bits */
85741d7544aSbluhm 		core_mask = (1U << core_bits) - 1;
858fccfca3aSdlg 		thread_id = apicid & core_mask;
859fccfca3aSdlg 
860fccfca3aSdlg 		/* Cut logical thread_id into core id, and smt id in a core */
861fccfca3aSdlg 		ci->ci_core_id = thread_id / nthreads;
862fccfca3aSdlg 		ci->ci_smt_id = thread_id % nthreads;
863ccd74f94Sguenther 	} else if (ci->ci_vendor == CPUV_INTEL) {
864ec99753cShaesbaert 		/* We only support leaf 1/4 detection */
865ccd74f94Sguenther 		if (ci->ci_cpuid_level < 4)
866ec99753cShaesbaert 			goto no_topology;
867ec99753cShaesbaert 		/* Get max_apicid */
868ec99753cShaesbaert 		CPUID(1, eax, ebx, ecx, edx);
869ec99753cShaesbaert 		max_apicid = (ebx >> 16) & 0xff;
870ec99753cShaesbaert 		/* Get max_coreid */
871ec99753cShaesbaert 		CPUID_LEAF(4, 0, eax, ebx, ecx, edx);
872ec99753cShaesbaert 		max_coreid = ((eax >> 26) & 0x3f) + 1;
873ec99753cShaesbaert 		/* SMT */
874ec99753cShaesbaert 		smt_bits = mask_width(max_apicid / max_coreid);
87541d7544aSbluhm 		smt_mask = (1U << smt_bits) - 1;
876ec99753cShaesbaert 		/* Core */
877ec99753cShaesbaert 		core_bits = log2(max_coreid);
87841d7544aSbluhm 		core_mask = (1U << (core_bits + smt_bits)) - 1;
879ec99753cShaesbaert 		core_mask ^= smt_mask;
880ec99753cShaesbaert 		/* Pkg */
881ec99753cShaesbaert 		pkg_bits = core_bits + smt_bits;
88241d7544aSbluhm 		pkg_mask = ~0U << core_bits;
883ec99753cShaesbaert 
884f4828eb5Ssthen 		ci->ci_smt_id = apicid & smt_mask;
885ec99753cShaesbaert 		ci->ci_core_id = (apicid & core_mask) >> smt_bits;
886ec99753cShaesbaert 		ci->ci_pkg_id = (apicid & pkg_mask) >> pkg_bits;
887ec99753cShaesbaert 	} else
888ec99753cShaesbaert 		goto no_topology;
889ec99753cShaesbaert #ifdef DEBUG
890ec99753cShaesbaert 	printf("cpu%d: smt %u, core %u, pkg %u "
891ec99753cShaesbaert 		"(apicid 0x%x, max_apicid 0x%x, max_coreid 0x%x, smt_bits 0x%x, smt_mask 0x%x, "
892ec99753cShaesbaert 		"core_bits 0x%x, core_mask 0x%x, pkg_bits 0x%x, pkg_mask 0x%x)\n",
893ec99753cShaesbaert 		ci->ci_cpuid, ci->ci_smt_id, ci->ci_core_id, ci->ci_pkg_id,
894ec99753cShaesbaert 		apicid, max_apicid, max_coreid, smt_bits, smt_mask, core_bits,
895ec99753cShaesbaert 		core_mask, pkg_bits, pkg_mask);
896ec99753cShaesbaert #else
897ec99753cShaesbaert 	printf("cpu%d: smt %u, core %u, package %u\n", ci->ci_cpuid,
898ec99753cShaesbaert 		ci->ci_smt_id, ci->ci_core_id, ci->ci_pkg_id);
899ec99753cShaesbaert 
900ec99753cShaesbaert #endif
901ec99753cShaesbaert 	return;
902ec99753cShaesbaert 	/* We can't map, so consider ci_core_id as ci_cpuid */
903ec99753cShaesbaert no_topology:
904ec99753cShaesbaert #endif
905ec99753cShaesbaert 	ci->ci_smt_id  = 0;
906ec99753cShaesbaert 	ci->ci_core_id = ci->ci_cpuid;
907ec99753cShaesbaert 	ci->ci_pkg_id  = 0;
908f5df1827Smickey }
90977d6d4a2Smlarkin 
9105fe96b9aSderaadt #if NVMM > 0
91177d6d4a2Smlarkin /*
91277d6d4a2Smlarkin  * cpu_check_vmm_cap
91377d6d4a2Smlarkin  *
91477d6d4a2Smlarkin  * Checks for VMM capabilities for 'ci'. Initializes certain per-cpu VMM
91577d6d4a2Smlarkin  * state in 'ci' if virtualization extensions are found.
91677d6d4a2Smlarkin  *
91777d6d4a2Smlarkin  * Parameters:
91877d6d4a2Smlarkin  *  ci: the cpu being checked
91977d6d4a2Smlarkin  */
92077d6d4a2Smlarkin void
92177d6d4a2Smlarkin cpu_check_vmm_cap(struct cpu_info *ci)
92277d6d4a2Smlarkin {
92377d6d4a2Smlarkin 	uint64_t msr;
924a937696cSmlarkin 	uint32_t cap, dummy, edx;
92577d6d4a2Smlarkin 
92677d6d4a2Smlarkin 	/*
92777d6d4a2Smlarkin 	 * Check for workable VMX
92877d6d4a2Smlarkin 	 */
92977d6d4a2Smlarkin 	if (cpu_ecxfeature & CPUIDECX_VMX) {
93077d6d4a2Smlarkin 		msr = rdmsr(MSR_IA32_FEATURE_CONTROL);
93177d6d4a2Smlarkin 
93277d6d4a2Smlarkin 		if (!(msr & IA32_FEATURE_CONTROL_LOCK))
93377d6d4a2Smlarkin 			ci->ci_vmm_flags |= CI_VMM_VMX;
93477d6d4a2Smlarkin 		else {
93577d6d4a2Smlarkin 			if (msr & IA32_FEATURE_CONTROL_VMX_EN)
93677d6d4a2Smlarkin 				ci->ci_vmm_flags |= CI_VMM_VMX;
937b4ff5abaSmartijn 			else
938b4ff5abaSmartijn 				ci->ci_vmm_flags |= CI_VMM_DIS;
93977d6d4a2Smlarkin 		}
94077d6d4a2Smlarkin 	}
94177d6d4a2Smlarkin 
94277d6d4a2Smlarkin 	/*
9430a1af29dSmlarkin 	 * Check for EPT (Intel Nested Paging) and other secondary
9440a1af29dSmlarkin 	 * controls
94577d6d4a2Smlarkin 	 */
94677d6d4a2Smlarkin 	if (ci->ci_vmm_flags & CI_VMM_VMX) {
94777d6d4a2Smlarkin 		/* Secondary controls available? */
94877d6d4a2Smlarkin 		/* XXX should we check true procbased ctls here if avail? */
94977d6d4a2Smlarkin 		msr = rdmsr(IA32_VMX_PROCBASED_CTLS);
95077d6d4a2Smlarkin 		if (msr & (IA32_VMX_ACTIVATE_SECONDARY_CONTROLS) << 32) {
95177d6d4a2Smlarkin 			msr = rdmsr(IA32_VMX_PROCBASED2_CTLS);
95277d6d4a2Smlarkin 			/* EPT available? */
95377d6d4a2Smlarkin 			if (msr & (IA32_VMX_ENABLE_EPT) << 32)
95477d6d4a2Smlarkin 				ci->ci_vmm_flags |= CI_VMM_EPT;
95577d6d4a2Smlarkin 		}
95677d6d4a2Smlarkin 	}
95777d6d4a2Smlarkin 
95877d6d4a2Smlarkin 	/*
95977d6d4a2Smlarkin 	 * Check startup config (VMX)
96077d6d4a2Smlarkin 	 */
96177d6d4a2Smlarkin 	if (ci->ci_vmm_flags & CI_VMM_VMX) {
96277d6d4a2Smlarkin 		/* CR0 fixed and flexible bits */
96377d6d4a2Smlarkin 		msr = rdmsr(IA32_VMX_CR0_FIXED0);
96477d6d4a2Smlarkin 		ci->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed0 = msr;
96577d6d4a2Smlarkin 		msr = rdmsr(IA32_VMX_CR0_FIXED1);
96677d6d4a2Smlarkin 		ci->ci_vmm_cap.vcc_vmx.vmx_cr0_fixed1 = msr;
96777d6d4a2Smlarkin 
96877d6d4a2Smlarkin 		/* CR4 fixed and flexible bits */
96977d6d4a2Smlarkin 		msr = rdmsr(IA32_VMX_CR4_FIXED0);
97077d6d4a2Smlarkin 		ci->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed0 = msr;
97177d6d4a2Smlarkin 		msr = rdmsr(IA32_VMX_CR4_FIXED1);
97277d6d4a2Smlarkin 		ci->ci_vmm_cap.vcc_vmx.vmx_cr4_fixed1 = msr;
97377d6d4a2Smlarkin 
97477d6d4a2Smlarkin 		/* VMXON region revision ID (bits 30:0 of IA32_VMX_BASIC) */
97577d6d4a2Smlarkin 		msr = rdmsr(IA32_VMX_BASIC);
97677d6d4a2Smlarkin 		ci->ci_vmm_cap.vcc_vmx.vmx_vmxon_revision =
97777d6d4a2Smlarkin 			(uint32_t)(msr & 0x7FFFFFFF);
97877d6d4a2Smlarkin 
97977d6d4a2Smlarkin 		/* MSR save / load table size */
98077d6d4a2Smlarkin 		msr = rdmsr(IA32_VMX_MISC);
98177d6d4a2Smlarkin 		ci->ci_vmm_cap.vcc_vmx.vmx_msr_table_size =
98277d6d4a2Smlarkin 			(uint32_t)(msr & IA32_VMX_MSR_LIST_SIZE_MASK) >> 25;
9832a5b1ebdSmlarkin 
9842a5b1ebdSmlarkin 		/* CR3 target count size */
9852a5b1ebdSmlarkin 		ci->ci_vmm_cap.vcc_vmx.vmx_cr3_tgt_count =
9862a5b1ebdSmlarkin 			(uint32_t)(msr & IA32_VMX_CR3_TGT_SIZE_MASK) >> 16;
98777d6d4a2Smlarkin 	}
98877d6d4a2Smlarkin 
98977d6d4a2Smlarkin 	/*
99077d6d4a2Smlarkin 	 * Check for workable SVM
99177d6d4a2Smlarkin 	 */
99277d6d4a2Smlarkin 	if (ecpu_ecxfeature & CPUIDECX_SVM) {
99377d6d4a2Smlarkin 		msr = rdmsr(MSR_AMD_VM_CR);
99477d6d4a2Smlarkin 
99577d6d4a2Smlarkin 		if (!(msr & AMD_SVMDIS))
99677d6d4a2Smlarkin 			ci->ci_vmm_flags |= CI_VMM_SVM;
997b49f7c39Smlarkin 
998a937696cSmlarkin 		CPUID(CPUID_AMD_SVM_CAP, dummy,
999a937696cSmlarkin 		    ci->ci_vmm_cap.vcc_svm.svm_max_asid, dummy, edx);
1000b49f7c39Smlarkin 
10012aa3e17eSmlarkin 		if (ci->ci_vmm_cap.vcc_svm.svm_max_asid > 0xFFF)
10022aa3e17eSmlarkin 			ci->ci_vmm_cap.vcc_svm.svm_max_asid = 0xFFF;
1003a937696cSmlarkin 
1004a937696cSmlarkin 		if (edx & AMD_SVM_FLUSH_BY_ASID_CAP)
1005a937696cSmlarkin 			ci->ci_vmm_cap.vcc_svm.svm_flush_by_asid = 1;
1006a937696cSmlarkin 
1007a937696cSmlarkin 		if (edx & AMD_SVM_VMCB_CLEAN_CAP)
1008a937696cSmlarkin 			ci->ci_vmm_cap.vcc_svm.svm_vmcb_clean = 1;
100918126f0eSdv 
101018126f0eSdv 		if (edx & AMD_SVM_DECODE_ASSIST_CAP)
101118126f0eSdv 			ci->ci_vmm_cap.vcc_svm.svm_decode_assist = 1;
101277d6d4a2Smlarkin 	}
101377d6d4a2Smlarkin 
101477d6d4a2Smlarkin 	/*
101577d6d4a2Smlarkin 	 * Check for SVM Nested Paging
101677d6d4a2Smlarkin 	 */
10177c3cbf8aSguenther 	if ((ci->ci_vmm_flags & CI_VMM_SVM) &&
10187c3cbf8aSguenther 	    ci->ci_pnfeatset >= CPUID_AMD_SVM_CAP) {
101977d6d4a2Smlarkin 		CPUID(CPUID_AMD_SVM_CAP, dummy, dummy, dummy, cap);
102077d6d4a2Smlarkin 		if (cap & AMD_SVM_NESTED_PAGING_CAP)
102177d6d4a2Smlarkin 			ci->ci_vmm_flags |= CI_VMM_RVI;
102277d6d4a2Smlarkin 	}
1023c844c4adSderaadt 
1024c844c4adSderaadt 	/*
1025c844c4adSderaadt 	 * Check "L1 flush on VM entry" (Intel L1TF vuln) semantics
1026d31beec1Sguenther 	 * Full details can be found here:
1027d31beec1Sguenther 	 * https://software.intel.com/security-software-guidance/insights/deep-dive-intel-analysis-l1-terminal-fault
1028c844c4adSderaadt 	 */
1029ccd74f94Sguenther 	if (ci->ci_vendor == CPUV_INTEL) {
1030c844c4adSderaadt 		if (ci->ci_feature_sefflags_edx & SEFF0EDX_L1DF)
1031c844c4adSderaadt 			ci->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr = 1;
1032c844c4adSderaadt 		else
1033c844c4adSderaadt 			ci->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr = 0;
1034c844c4adSderaadt 
1035c844c4adSderaadt 		/*
1036c844c4adSderaadt 		 * Certain CPUs may have the vulnerability remedied in
1037d31beec1Sguenther 		 * hardware (RDCL_NO), or we may be nested in an VMM that
1038d31beec1Sguenther 		 * is doing flushes (SKIP_L1DFL_VMENTRY) using the MSR.
1039d31beec1Sguenther 		 * In either case no mitigation at all is necessary.
1040c844c4adSderaadt 		 */
1041c844c4adSderaadt 		if (ci->ci_feature_sefflags_edx & SEFF0EDX_ARCH_CAP) {
1042c844c4adSderaadt 			msr = rdmsr(MSR_ARCH_CAPABILITIES);
10439ff0cc92Sguenther 			if ((msr & ARCH_CAP_RDCL_NO) ||
10449ff0cc92Sguenther 			    ((msr & ARCH_CAP_SKIP_L1DFL_VMENTRY) &&
1045d31beec1Sguenther 			    ci->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr))
1046c844c4adSderaadt 				ci->ci_vmm_cap.vcc_vmx.vmx_has_l1_flush_msr =
1047c844c4adSderaadt 				    VMX_SKIP_L1D_FLUSH;
1048c844c4adSderaadt 		}
1049c844c4adSderaadt 	}
105077d6d4a2Smlarkin }
10515fe96b9aSderaadt #endif /* NVMM > 0 */
1052