xref: /illumos-gate/usr/src/lib/libc/amd64/gen/proc64_id.c (revision e121b61f5e8ffbeb2f6b373c967c80351333ee21)
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 
42 struct 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  */
54 static void
55 get_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  */
113 static void
114 get_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  */
170 void
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