1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2009, Intel Corporation.
24 * All rights reserved.
25 */
26
27/*
28 * Portions Copyright 2009 Advanced Micro Devices, Inc.
29 */
30
31#include <sys/types.h>
32#include "proc64_id.h"
33
34/*
35 * Intel cpuid eax=4 Cache Types
36 */
37#define	NULL_CACHE		0x0
38#define	DATA_CACHE		0x1
39#define	INSTRUCTION_CACHE	0x2
40#define	UNIFIED_CACHE		0x3
41
42struct cpuid_values {
43	uint_t eax;
44	uint_t ebx;
45	uint_t ecx;
46	uint_t edx;
47};
48
49/*
50 * get_intel_cache_info()
51 *	Get cpu cache sizes for optimized 64-bit libc functions mem* and str*.
52 *	Find the sizes of the 1st, 2nd and largest level caches.
53 */
54static void
55get_intel_cache_info(void)
56{
57	int cache_level;
58	int largest_cache_level = 0;
59	int cache_index = 0;
60	int cache_type;
61	int line_size, partitions, ways, sets;
62	uint_t cache_size;
63	uint_t l1_cache_size = 0;
64	uint_t l2_cache_size = 0;
65	uint_t largest_level_cache = 0;
66	struct cpuid_values cpuid_info;
67
68	while (1) {
69		__libc_get_cpuid(4, (uint_t *)&cpuid_info, cache_index);
70
71		cache_type = cpuid_info.eax & 0x1f;
72		if (cache_type == NULL_CACHE) {
73			/*
74			 * No more caches.
75			 */
76			break;
77		}
78		cache_index += 1;
79
80		if (cache_type == INSTRUCTION_CACHE) {
81			/*
82			 * Don't care for memops
83			 */
84			continue;
85		}
86
87		cache_level = (cpuid_info.eax >> 0x5) & 0x7;
88		line_size = (cpuid_info.ebx & 0xfff) + 1;
89		partitions = ((cpuid_info.ebx >> 12) & 0x3ff) + 1;
90		ways = ((cpuid_info.ebx >> 22) & 0x3ff) + 1;
91		sets = cpuid_info.ecx + 1;
92		cache_size = ways * partitions * line_size * sets;
93
94		if (cache_level == 1) {
95			l1_cache_size = cache_size;
96		}
97		if (cache_level == 2) {
98			l2_cache_size = cache_size;
99		}
100		if (cache_level > largest_cache_level) {
101			largest_cache_level = cache_level;
102			largest_level_cache = cache_size;
103		}
104	}
105
106	__set_cache_sizes(l1_cache_size, l2_cache_size, largest_level_cache);
107}
108
109/*
110 * get_amd_cache_info()
111 *      Same as get_intel_cache_info() but for AMD processors
112 */
113static void
114get_amd_cache_info(void)
115{
116	uint_t l1_cache_size = AMD_DFLT_L1_CACHE_SIZE;
117	uint_t l2_cache_size = AMD_DFLT_L2_CACHE_SIZE;
118	uint_t l3_cache_size = 0;
119	uint_t largest_level_cache = 0;
120	struct cpuid_values cpuid_info;
121	uint_t maxeax;
122	int ncores;
123
124	cpuid_info.eax = 0;
125	__libc_get_cpuid(0x80000000, (uint_t *)&cpuid_info, -1);
126	maxeax = cpuid_info.eax;
127
128	if (maxeax >= 0x80000005) {	/* We have L1D info */
129		__libc_get_cpuid(0x80000005, (uint_t *)&cpuid_info, -1);
130		l1_cache_size = ((cpuid_info.ecx >> 24) & 0xff) * 1024;
131	}
132
133	if (maxeax >= 0x80000006) {	/* We have L2 and L3 info */
134		__libc_get_cpuid(0x80000006, (uint_t *)&cpuid_info, -1);
135		l2_cache_size = ((cpuid_info.ecx >> 16) & 0xffff) * 1024;
136		l3_cache_size = ((cpuid_info.edx >> 18) & 0x3fff) * 512 * 1024;
137	}
138
139	/*
140	 * L3 cache is shared between cores on the processor
141	 */
142	if (maxeax >= 0x80000008 && l3_cache_size != 0) {
143		largest_level_cache = l3_cache_size;
144
145		/*
146		 * Divide by number of cores on the processor
147		 */
148		__libc_get_cpuid(0x80000008, (uint_t *)&cpuid_info, -1);
149		ncores = (cpuid_info.ecx & 0xff) + 1;
150		if (ncores > 1)
151			largest_level_cache /= ncores;
152
153		/*
154		 * L3 is a victim cache for L2
155		 */
156		largest_level_cache += l2_cache_size;
157	} else {
158		largest_level_cache = l2_cache_size;
159	}
160
161	__set_cache_sizes(l1_cache_size, l2_cache_size,
162	    largest_level_cache);
163}
164
165/*
166 * proc64_id()
167 *	Determine cache and SSE level to use for memops and strops specific to
168 *	processor type.
169 */
170void
171__proc64id(void)
172{
173	int use_sse = NO_SSE;
174	struct cpuid_values cpuid_info;
175
176	__libc_get_cpuid(0, &cpuid_info, 0);
177
178	/*
179	 * Check for AuthenticAMD
180	 */
181	if ((cpuid_info.ebx == 0x68747541) && /* Auth */
182	    (cpuid_info.edx == 0x69746e65) && /* enti */
183	    (cpuid_info.ecx == 0x444d4163)) { /* cAMD */
184		get_amd_cache_info();
185		return;
186	}
187
188	/*
189	 * Check for GenuineIntel
190	 */
191	if ((cpuid_info.ebx != 0x756e6547) || /* Genu */
192	    (cpuid_info.edx != 0x49656e69) || /* ineI */
193	    (cpuid_info.ecx != 0x6c65746e)) { /* ntel */
194		/*
195		 * Not Intel - use defaults.
196		 */
197		return;
198	}
199
200	/*
201	 * Genuine Intel
202	 */
203
204	/*
205	 * Look for CPUID function 4 support - Deterministic Cache Parameters.
206	 * Otherwise use default cache sizes.
207	 */
208	if (cpuid_info.eax >= 4) {
209		get_intel_cache_info();
210
211		/*
212		 * Check what SSE versions are supported.
213		 */
214		__libc_get_cpuid(1, &cpuid_info, 0);
215		if (cpuid_info.ecx & CPUID_INTC_ECX_SSE4_2) {
216			use_sse |= USE_SSE4_2;
217		}
218		if (cpuid_info.ecx & CPUID_INTC_ECX_SSE4_1) {
219			use_sse |= USE_SSE4_1;
220		}
221		if (cpuid_info.ecx & CPUID_INTC_ECX_SSSE3) {
222			use_sse |= USE_SSSE3;
223		}
224		if (cpuid_info.ecx & CPUID_INTC_ECX_SSE3) {
225			use_sse |= USE_SSE3;
226		}
227		if (cpuid_info.edx & CPUID_INTC_EDX_SSE2) {
228			use_sse |= USE_SSE2;
229		}
230		use_sse |= USE_BSF;
231		__intel_set_memops_method(use_sse);
232	} else {
233		__set_cache_sizes(INTEL_DFLT_L1_CACHE_SIZE,
234		    INTEL_DFLT_L2_CACHE_SIZE,
235		    INTEL_DFLT_LARGEST_CACHE_SIZE);
236		__intel_set_memops_method(use_sse);
237	}
238}
239