blob: 18f5299cd93c477c6d9af0d2a6d4cf05b4d69f14 [file] [log] [blame]
Martin Roth9b1b3352016-02-24 12:27:06 -08001/*
2 * MemTest86+ V5 Specific code (GPL V2.0)
3 * By Samuel DEMEULEMEESTER, sdemeule@memtest.org
4 * http://www.canardpc.com - http://www.memtest.org
5 * ------------------------------------------------
6 * init.c - MemTest-86 Version 3.6
7 *
8 * Released under version 2 of the Gnu Public License.
9 * By Chris Brady
Martin Roth869474b2016-02-24 13:47:46 -080010 *
11 * Edited by David McInnis October 4, 2014
Martin Roth9b1b3352016-02-24 12:27:06 -080012 */
Martin Roth4dcd13d2016-02-24 13:53:07 -080013
Martin Roth9b1b3352016-02-24 12:27:06 -080014
15#include "stdin.h"
16#include "stddef.h"
17#include "test.h"
18#include "defs.h"
19#include "config.h"
20#include "cpuid.h"
21#include "smp.h"
22#include "io.h"
23#include "spd.h"
Martin Roth8cc1aeb2016-02-24 13:03:52 -080024#include "multiboot.h"
Martin Roth1286f192016-02-25 10:26:59 -080025#include "controller.h"
26#include "pci.h"
Martin Roth9b1b3352016-02-24 12:27:06 -080027
28extern struct tseq tseq[];
29extern short memsz_mode;
30extern int num_cpus;
31extern int act_cpus;
32extern int found_cpus;
33unsigned long imc_type = 0;
34extern int maxcpus;
35extern char cpu_mask[];
36extern void initialise_cpus();
37
38/* Here we store all of the cpuid data */
39extern struct cpu_ident cpu_id;
40
41int l1_cache=0, l2_cache=0, l3_cache=0;
42int tsc_invariable = 0;
43ulong extclock;
44
45ulong memspeed(ulong src, ulong len, int iter);
46static void cpu_type(void);
47static int cpuspeed(void);
48static void get_cache_size();
49static void cpu_cache_speed();
50void get_cpuid();
51int beepmode;
52extern short dmi_initialized;
53extern int dmi_err_cnts[MAX_DMI_MEMDEVS];
54
55/* Failsafe function */
56/* msec: number of ms to wait - scs: scancode expected to stop */
57/* bits: 0 = extended detection - 1: SMP - 2: Temp Check */
58/* 3: MP SMP - 4-7: RSVD */
59void failsafe(int msec, int scs)
60{
61 int i;
62 ulong sh, sl, l, h, t;
63 unsigned char c;
64 volatile char *pp;
Martin Roth4dcd13d2016-02-24 13:53:07 -080065
Ben Gardner90f7d112016-03-15 15:25:22 -050066 for (i=0, pp=(char *)(SCREEN_ADR+(18*160)+(18*2)+1); i<40; i++, pp+=2) {
Martin Roth9b1b3352016-02-24 12:27:06 -080067 *pp = 0x1E;
Martin Roth4dcd13d2016-02-24 13:53:07 -080068 }
Ben Gardner90f7d112016-03-15 15:25:22 -050069 for (i=0, pp=(char *)(SCREEN_ADR+(18*160)+(18*2)+1); i<3; i++, pp+=2) {
Martin Roth9b1b3352016-02-24 12:27:06 -080070 *pp = 0x9E;
Martin Roth4dcd13d2016-02-24 13:53:07 -080071 }
Ben Gardner90f7d112016-03-15 15:25:22 -050072 for (i=0, pp=(char *)(SCREEN_ADR+(18*160)+(55*2)+1); i<3; i++, pp+=2) {
Martin Roth9b1b3352016-02-24 12:27:06 -080073 *pp = 0x9E;
Martin Roth4dcd13d2016-02-24 13:53:07 -080074 }
75
Martin Roth9b1b3352016-02-24 12:27:06 -080076 cprint(18, 18, "==> Press F1 to enter Fail-Safe Mode <==");
Martin Roth4dcd13d2016-02-24 13:53:07 -080077
Ben Gardner90f7d112016-03-15 15:25:22 -050078 if (v->fail_safe & 2) {
79 cprint(19, 15, "==> Press F2 to force Multi-Threading (SMP) <==");
Martin Roth9b1b3352016-02-24 12:27:06 -080080 }
81
82 /* save the starting time */
Ben Gardner90f7d112016-03-15 15:25:22 -050083 asm __volatile__ (
84 "rdtsc" : "=a" (sl), "=d" (sh));
Martin Roth9b1b3352016-02-24 12:27:06 -080085
86 /* loop for n seconds */
87 while (1) {
Ben Gardner90f7d112016-03-15 15:25:22 -050088 asm __volatile__ (
89 "rdtsc" : "=a" (l), "=d" (h));
Martin Roth9b1b3352016-02-24 12:27:06 -080090 asm __volatile__ (
91 "subl %2,%0\n\t"
92 "sbbl %3,%1"
Ben Gardner90f7d112016-03-15 15:25:22 -050093 : "=a" (l), "=d" (h)
94 : "g" (sl), "g" (sh),
95 "0" (l), "1" (h));
Martin Roth9b1b3352016-02-24 12:27:06 -080096
97 t = h * ((unsigned)0xffffffff / v->clks_msec);
98 t += (l / v->clks_msec);
99
100 /* Is the time up? */
Ben Gardner90f7d112016-03-15 15:25:22 -0500101 if (t >= msec) { break; }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800102
Martin Roth9b1b3352016-02-24 12:27:06 -0800103 /* Is expected Scan code pressed? */
104 c = get_key();
105 c &= 0x7f;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800106
Martin Roth9b1b3352016-02-24 12:27:06 -0800107 /* F1 */
Ben Gardner90f7d112016-03-15 15:25:22 -0500108 if (c == scs) { v->fail_safe |= 1; break; }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800109
Martin Roth9b1b3352016-02-24 12:27:06 -0800110 /* F2 */
Ben Gardner90f7d112016-03-15 15:25:22 -0500111 if (c == scs+1) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800112 v->fail_safe ^= 2;
113 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800114 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800115
Martin Roth9b1b3352016-02-24 12:27:06 -0800116 /* F3 */
Ben Gardner90f7d112016-03-15 15:25:22 -0500117 if (c == scs+2) {
118 if (v->fail_safe & 2) { v->fail_safe ^= 2; }
Martin Roth9b1b3352016-02-24 12:27:06 -0800119 v->fail_safe |= 8;
120 break;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800121 }
Martin Roth9b1b3352016-02-24 12:27:06 -0800122 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800123
Martin Roth9b1b3352016-02-24 12:27:06 -0800124 cprint(18, 18, " ");
125 cprint(19, 15, " ");
Martin Roth4dcd13d2016-02-24 13:53:07 -0800126
Ben Gardner90f7d112016-03-15 15:25:22 -0500127 for (i=0, pp=(char *)(SCREEN_ADR+(18*160)+(18*2)+1); i<40; i++, pp+=2) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800128 *pp = 0x17;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800129 }
Martin Roth9b1b3352016-02-24 12:27:06 -0800130}
131
132
133
134static void display_init(void)
135{
136 int i;
137 volatile char *pp;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800138
Martin Roth9b1b3352016-02-24 12:27:06 -0800139 /* Set HW cursor out of screen boundaries */
140 __outb(0x0F, 0x03D4);
141 __outb(0xFF, 0x03D5);
142
143 __outb(0x0E, 0x03D4);
144 __outb(0xFF, 0x03D5);
145
146
147 serial_echo_init();
Ben Gardnerb72a23a2016-03-04 17:40:38 -0600148 serial_echo_print("\x1b[LINE_SCROLL;24r"); /* Set scroll area row 7-23 */
149 serial_echo_print("\x1b[H\x1b[2J"); /* Clear Screen */
150 serial_echo_print("\x1b[37m\x1b[44m");
151 serial_echo_print("\x1b[0m");
152 serial_echo_print("\x1b[37m\x1b[44m");
Martin Roth9b1b3352016-02-24 12:27:06 -0800153
154 /* Clear screen & set background to blue */
Ben Gardner90f7d112016-03-15 15:25:22 -0500155 for (i=0, pp=(char *)(SCREEN_ADR); i<80*24; i++) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800156 *pp++ = ' ';
157 *pp++ = 0x17;
158 }
159
160 /* Make the name background green */
Ben Gardner90f7d112016-03-15 15:25:22 -0500161 for (i=0, pp=(char *)(SCREEN_ADR+1); i<TITLE_WIDTH; i++, pp+=2) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800162 *pp = 0x20;
163 }
Martin Roth48c7f182016-02-25 16:24:37 -0800164 cprint(0, 0, MEMTEST_VERSION_STRING);
Martin Roth9b1b3352016-02-24 12:27:06 -0800165
166 /* Set Blinking "+" */
Martin Roth48c7f182016-02-25 16:24:37 -0800167 pp=(char *)(SCREEN_ADR+1 + (MEMTEST_PLUS_LOCATION * 2));
168 *pp = 0xA4;
Martin Roth9b1b3352016-02-24 12:27:06 -0800169
170 /* Do reverse video for the bottom display line */
Ben Gardner90f7d112016-03-15 15:25:22 -0500171 for (i=0, pp=(char *)(SCREEN_ADR+1+(24 * 160)); i<80; i++, pp+=2) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800172 *pp = 0x71;
173 }
174
Ben Gardnerb72a23a2016-03-04 17:40:38 -0600175 serial_echo_print("\x1b[0m");
Martin Roth9b1b3352016-02-24 12:27:06 -0800176}
177
178/*
179 * Initialize test, setup screen and find out how much memory there is.
180 */
181void init(void)
182{
183 int i;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800184
Martin Roth9b1b3352016-02-24 12:27:06 -0800185 outb(0x8, 0x3f2); /* Kill Floppy Motor */
186
187 /* Turn on cache */
188 set_cache(1);
189
190 /* Setup the display */
191 display_init();
Martin Roth4dcd13d2016-02-24 13:53:07 -0800192
Martin Roth9b1b3352016-02-24 12:27:06 -0800193 cprint(5, 60, "| Time: 0:00:00");
Ben Gardner90f7d112016-03-15 15:25:22 -0500194 cprint(1, COL_MID, "Pass %");
195 cprint(2, COL_MID, "Test %");
196 cprint(3, COL_MID, "Test #");
197 cprint(4, COL_MID, "Testing: ");
198 cprint(5, COL_MID, "Pattern: ");
Martin Roth9b1b3352016-02-24 12:27:06 -0800199 cprint(1, 0, "CLK: (32b Mode)");
200 cprint(2, 0, "L1 Cache: Unknown ");
201 cprint(3, 0, "L2 Cache: Unknown ");
Ben Gardner90f7d112016-03-15 15:25:22 -0500202 cprint(4, 0, "L3 Cache: None ");
203 cprint(5, 0, "Memory : ");
204 cprint(6, 0, "------------------------------------------------------------------------------");
Martin Roth9b1b3352016-02-24 12:27:06 -0800205 cprint(7, 0, "Core#:");
206 cprint(8, 0, "State:");
207 cprint(9, 0, "Cores: Active / Total (Run: All) | Pass: 0 Errors: 0 ");
208 cprint(10, 0, "------------------------------------------------------------------------------");
209
Martin Roth4dcd13d2016-02-24 13:53:07 -0800210 /*
Martin Roth9b1b3352016-02-24 12:27:06 -0800211 for(i=0, pp=(char *)(SCREEN_ADR+(5*160)+(53*2)+1); i<20; i++, pp+=2) {
212 *pp = 0x92;
213 }
214
215 for(i=0, pp=(char *)(SCREEN_ADR+0*160+1); i<80; i++, pp+=2) {
216 *pp = 0x47;
217 }
218 */
Martin Roth4dcd13d2016-02-24 13:53:07 -0800219
Martin Roth9b1b3352016-02-24 12:27:06 -0800220 cprint(7, 39, "| Chipset : Unknown");
221 cprint(8, 39, "| Memory Type : Unknown");
Martin Roth4dcd13d2016-02-24 13:53:07 -0800222
Martin Roth9b1b3352016-02-24 12:27:06 -0800223
Ben Gardner90f7d112016-03-15 15:25:22 -0500224 for (i=0; i < 6; i++) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800225 cprint(i, COL_MID-2, "| ");
226 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800227
Martin Roth9b1b3352016-02-24 12:27:06 -0800228 footer();
229
Ben Gardner90f7d112016-03-15 15:25:22 -0500230 aprint(5, 10, v->test_pages);
Martin Roth9b1b3352016-02-24 12:27:06 -0800231
Ben Gardner90f7d112016-03-15 15:25:22 -0500232 v->pass = 0;
233 v->msg_line = 0;
234 v->ecount = 0;
235 v->ecc_ecount = 0;
Martin Roth9b1b3352016-02-24 12:27:06 -0800236 v->testsel = -1;
237 v->msg_line = LINE_SCROLL-1;
238 v->scroll_start = v->msg_line * 160;
239 v->erri.low_addr.page = 0x7fffffff;
240 v->erri.low_addr.offset = 0xfff;
241 v->erri.high_addr.page = 0;
242 v->erri.high_addr.offset = 0;
243 v->erri.min_bits = 32;
244 v->erri.max_bits = 0;
245 v->erri.min_bits = 32;
246 v->erri.max_bits = 0;
247 v->erri.maxl = 0;
248 v->erri.cor_err = 0;
249 v->erri.ebits = 0;
250 v->erri.hdr_flag = 0;
251 v->erri.tbits = 0;
252 for (i=0; tseq[i].msg != NULL; i++) {
253 tseq[i].errors = 0;
254 }
255 if (dmi_initialized) {
Ben Gardner90f7d112016-03-15 15:25:22 -0500256 for (i=0; i < MAX_DMI_MEMDEVS; i++) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800257 if (dmi_err_cnts[i] > 0) {
258 dmi_err_cnts[i] = 0;
259 }
260 }
261 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800262
Martin Roth9b1b3352016-02-24 12:27:06 -0800263 /* setup beep mode */
264 beepmode = BEEP_MODE;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800265
Martin Roth9b1b3352016-02-24 12:27:06 -0800266 /* Get the cpu and cache information */
267 get_cpuid();
268
269 /* setup pci */
Martin Roth4dcd13d2016-02-24 13:53:07 -0800270 pci_init();
Martin Roth9b1b3352016-02-24 12:27:06 -0800271
Martin Roth4dcd13d2016-02-24 13:53:07 -0800272 get_cache_size();
Martin Roth9b1b3352016-02-24 12:27:06 -0800273
274 cpu_type();
275
276 cpu_cache_speed();
277
Ben Gardner90f7d112016-03-15 15:25:22 -0500278 /* Check fail safe */
Martin Roth9b1b3352016-02-24 12:27:06 -0800279 failsafe(5000, 0x3B);
280
281 /* Initalize SMP */
282 initialise_cpus();
Martin Roth4dcd13d2016-02-24 13:53:07 -0800283
Martin Roth9b1b3352016-02-24 12:27:06 -0800284 for (i = 0; i <num_cpus; i++) {
285 dprint(7, i+7, i%10, 1, 0);
286 cprint(8, i+7, "S");
287 }
288
289 dprint(9, 19, num_cpus, 2, 0);
Martin Roth4dcd13d2016-02-24 13:53:07 -0800290
Ben Gardner90f7d112016-03-15 15:25:22 -0500291 if ((v->fail_safe & 3) == 2) {
292 cprint(LINE_CPU, 9, "(SMP: Disabled)");
293 cprint(LINE_RAM, 9, "Running...");
Martin Roth9b1b3352016-02-24 12:27:06 -0800294 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800295 // dprint(10, 5, found_cpus, 2, 0);
Martin Roth9b1b3352016-02-24 12:27:06 -0800296
297 /* Find Memory Specs */
Ben Gardner90f7d112016-03-15 15:25:22 -0500298 if (v->fail_safe & 1) {
299 cprint(LINE_CPU, COL_SPEC, " **** FAIL SAFE **** FAIL SAFE **** ");
300 cprint(LINE_RAM, COL_SPEC, " No detection, same reliability ");
301 } else {
302 find_controller();
303 get_spd_spec();
304 if (num_cpus <= 16 && !(v->fail_safe & 4)) { coretemp(); }
305 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800306
Ben Gardner90f7d112016-03-15 15:25:22 -0500307 if (v->check_temp > 0 && !(v->fail_safe & 4)) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800308 cprint(LINE_CPU, 26, "| CPU Temp");
Martin Rothfb4ff612016-02-25 16:27:04 -0800309 cprint(LINE_CPU+1, 26, "| C");
Martin Roth9b1b3352016-02-24 12:27:06 -0800310 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800311
Ben Gardner90f7d112016-03-15 15:25:22 -0500312 beep(600);
313 beep(1000);
Martin Roth4dcd13d2016-02-24 13:53:07 -0800314
Martin Roth9b1b3352016-02-24 12:27:06 -0800315 /* Record the start time */
Ben Gardner90f7d112016-03-15 15:25:22 -0500316 asm __volatile__ ("rdtsc" : "=a" (v->startl), "=d" (v->starth));
317 v->snapl = v->startl;
318 v->snaph = v->starth;
Martin Roth9b1b3352016-02-24 12:27:06 -0800319 if (l1_cache == 0) { l1_cache = 64; }
320 if (l2_cache == 0) { l1_cache = 512; }
321 v->printmode=PRINTMODE_ADDRESSES;
322 v->numpatn=0;
323}
324
325/* Get cache sizes for most AMD and Intel CPUs, exceptions for old CPUs are
326 * handled in CPU detection */
327void get_cache_size()
328{
329 int i, j, n, size;
330 unsigned int v[4];
331 unsigned char *dp = (unsigned char *)v;
332 struct cpuid4_eax *eax = (struct cpuid4_eax *)&v[0];
333 struct cpuid4_ebx *ebx = (struct cpuid4_ebx *)&v[1];
334 struct cpuid4_ecx *ecx = (struct cpuid4_ecx *)&v[2];
335
Ben Gardner90f7d112016-03-15 15:25:22 -0500336 switch (cpu_id.vend_id.char_array[0]) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800337 /* AMD Processors */
338 case 'A':
339 //l1_cache = cpu_id.cache_info.amd.l1_i_sz;
340 l1_cache = cpu_id.cache_info.amd.l1_d_sz;
341 l2_cache = cpu_id.cache_info.amd.l2_sz;
342 l3_cache = cpu_id.cache_info.amd.l3_sz;
Ben Gardner90f7d112016-03-15 15:25:22 -0500343 l3_cache *= 512;
Martin Roth9b1b3352016-02-24 12:27:06 -0800344 break;
345 case 'G':
346 /* Intel Processors */
347 l1_cache = 0;
348 l2_cache = 0;
349 l3_cache = 0;
350
351 /* Use CPUID(4) if it is available */
352 if (cpu_id.max_cpuid > 3) {
Ben Gardner90f7d112016-03-15 15:25:22 -0500353 /* figure out how many cache leaves */
354 n = -1;
355 do {
356 ++n;
357 /* Do cpuid(4) loop to find out num_cache_leaves */
358 cpuid_count(4, n, &v[0], &v[1], &v[2], &v[3]);
359 } while ((eax->ctype) != 0);
Martin Roth9b1b3352016-02-24 12:27:06 -0800360
Ben Gardner90f7d112016-03-15 15:25:22 -0500361 /* loop through all of the leaves */
362 for (i=0; i<n; i++) {
363 cpuid_count(4, i, &v[0], &v[1], &v[2], &v[3]);
Martin Roth9b1b3352016-02-24 12:27:06 -0800364
Ben Gardner90f7d112016-03-15 15:25:22 -0500365 /* Check for a valid cache type */
366 if (eax->ctype == 1 || eax->ctype == 3) {
367 /* Compute the cache size */
368 size = (ecx->number_of_sets + 1) *
369 (ebx->coherency_line_size + 1) *
370 (ebx->physical_line_partition + 1) *
371 (ebx->ways_of_associativity + 1);
372 size /= 1024;
Martin Roth9b1b3352016-02-24 12:27:06 -0800373
Ben Gardner90f7d112016-03-15 15:25:22 -0500374 switch (eax->level) {
375 case 1:
376 l1_cache += size;
377 break;
378 case 2:
379 l2_cache += size;
380 break;
381 case 3:
382 l3_cache += size;
383 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800384 }
Ben Gardner90f7d112016-03-15 15:25:22 -0500385 }
386 }
387 return;
Martin Roth9b1b3352016-02-24 12:27:06 -0800388 }
389
390 /* No CPUID(4) so we use the older CPUID(2) method */
391 /* Get number of times to iterate */
392 cpuid(2, &v[0], &v[1], &v[2], &v[3]);
393 n = v[0] & 0xff;
Ben Gardner90f7d112016-03-15 15:25:22 -0500394 for (i=0; i<n; i++) {
395 cpuid(2, &v[0], &v[1], &v[2], &v[3]);
Martin Roth9b1b3352016-02-24 12:27:06 -0800396
Ben Gardner90f7d112016-03-15 15:25:22 -0500397 /* If bit 31 is set, this is an unknown format */
398 for (j=0; j<3; j++) {
399 if (v[j] & (1 << 31)) {
400 v[j] = 0;
401 }
402 }
Martin Roth9b1b3352016-02-24 12:27:06 -0800403
Ben Gardner90f7d112016-03-15 15:25:22 -0500404 /* Byte 0 is level count, not a descriptor */
405 for (j = 1; j < 16; j++) {
406 switch (dp[j]) {
407 case 0x6:
408 case 0xa:
409 case 0x66:
410 l1_cache += 8;
411 break;
412 case 0x8:
413 case 0xc:
414 case 0xd:
415 case 0x60:
416 case 0x67:
417 l1_cache += 16;
418 break;
419 case 0xe:
420 l1_cache += 24;
421 break;
422 case 0x9:
423 case 0x2c:
424 case 0x30:
425 case 0x68:
426 l1_cache += 32;
427 break;
428 case 0x39:
429 case 0x3b:
430 case 0x41:
431 case 0x79:
432 l2_cache += 128;
433 break;
434 case 0x3a:
435 l2_cache += 192;
436 break;
437 case 0x21:
438 case 0x3c:
439 case 0x3f:
440 case 0x42:
441 case 0x7a:
442 case 0x82:
443 l2_cache += 256;
444 break;
445 case 0x3d:
446 l2_cache += 384;
447 break;
448 case 0x3e:
449 case 0x43:
450 case 0x7b:
451 case 0x7f:
452 case 0x80:
453 case 0x83:
454 case 0x86:
455 l2_cache += 512;
456 break;
457 case 0x44:
458 case 0x78:
459 case 0x7c:
460 case 0x84:
461 case 0x87:
462 l2_cache += 1024;
463 break;
464 case 0x45:
465 case 0x7d:
466 case 0x85:
467 l2_cache += 2048;
468 break;
469 case 0x48:
470 l2_cache += 3072;
471 break;
472 case 0x4e:
473 l2_cache += 6144;
474 break;
475 case 0x23:
476 case 0xd0:
477 l3_cache += 512;
478 break;
479 case 0xd1:
480 case 0xd6:
481 l3_cache += 1024;
482 break;
483 case 0x25:
484 case 0xd2:
485 case 0xd7:
486 case 0xdc:
487 case 0xe2:
488 l3_cache += 2048;
489 break;
490 case 0x29:
491 case 0x46:
492 case 0x49:
493 case 0xd8:
494 case 0xdd:
495 case 0xe3:
496 l3_cache += 4096;
497 break;
498 case 0x4a:
499 l3_cache += 6144;
500 break;
501 case 0x47:
502 case 0x4b:
503 case 0xde:
504 case 0xe4:
505 l3_cache += 8192;
506 break;
507 case 0x4c:
508 case 0xea:
509 l3_cache += 12288;
510 break;
511 case 0x4d:
512 l3_cache += 16384;
513 break;
514 case 0xeb:
515 l3_cache += 18432;
516 break;
517 case 0xec:
518 l3_cache += 24576;
519 break;
520 } /* end switch */
521 } /* end for 1-16 */
Martin Roth9b1b3352016-02-24 12:27:06 -0800522 } /* end for 0 - n */
523 }
524}
525
526/*
527 * Find IMC type and set global variables accordingly
528 */
529void detect_imc(void)
530{
531 // Check AMD IMC
Ben Gardner90f7d112016-03-15 15:25:22 -0500532 if (cpu_id.vend_id.char_array[0] == 'A' && cpu_id.vers.bits.family == 0xF) {
533 switch (cpu_id.vers.bits.extendedFamily) {
534 case 0x0:
535 imc_type = 0x0100; // Old K8
536 break;
537 case 0x1:
538 case 0x2:
539 imc_type = 0x0101; // K10 (Family 10h & 11h)
540 break;
541 case 0x3:
542 imc_type = 0x0102; // A-Series APU (Family 12h)
543 break;
544 case 0x5:
545 imc_type = 0x0103; // C- / E- / Z- Series APU (Family 14h)
546 break;
547 case 0x6:
548 imc_type = 0x0104; // FX Series (Family 15h)
549 break;
550 case 0x7:
551 imc_type = 0x0105; // Kabini & related (Family 16h)
552 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800553 }
Ben Gardner90f7d112016-03-15 15:25:22 -0500554 return;
555 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800556
557 // Check Intel IMC
Ben Gardner90f7d112016-03-15 15:25:22 -0500558 if (cpu_id.vend_id.char_array[0] == 'G' && cpu_id.vers.bits.family == 6 && cpu_id.vers.bits.extendedModel) {
559 switch (cpu_id.vers.bits.model) {
560 case 0x5:
561 if (cpu_id.vers.bits.extendedModel == 2) { imc_type = 0x0003; } // Core i3/i5 1st Gen 45 nm (NHM)
562 if (cpu_id.vers.bits.extendedModel == 3) { v->fail_safe |= 4; } // Atom Clover Trail
563 if (cpu_id.vers.bits.extendedModel == 4) { imc_type = 0x0007; } // HSW-ULT
564 break;
565 case 0x6:
566 if (cpu_id.vers.bits.extendedModel == 3) {
567 imc_type = 0x0009; // Atom Cedar Trail
568 v->fail_safe |= 4; // Disable Core temp
Martin Roth9b1b3352016-02-24 12:27:06 -0800569 }
Ben Gardner90f7d112016-03-15 15:25:22 -0500570 break;
571 case 0x7:
572 if (cpu_id.vers.bits.extendedModel == 3) {
573 imc_type = 0x000A; // Atom Bay Trail
574 }
575 break;
576 case 0xA:
577 switch (cpu_id.vers.bits.extendedModel) {
578 case 0x1:
579 imc_type = 0x0001; // Core i7 1st Gen 45 nm (NHME)
580 break;
581 case 0x2:
582 imc_type = 0x0004; // Core 2nd Gen (SNB)
583 break;
584 case 0x3:
585 imc_type = 0x0006; // Core 3nd Gen (IVB)
586 break;
587 }
588 break;
589 case 0xC:
590 switch (cpu_id.vers.bits.extendedModel) {
591 case 0x1:
592 if (cpu_id.vers.bits.stepping > 9) { imc_type = 0x0008; } // Atom PineView
593 v->fail_safe |= 4; // Disable Core temp
594 break;
595 case 0x2:
596 imc_type = 0x0002; // Core i7 1st Gen 32 nm (WMR)
597 break;
598 case 0x3:
599 imc_type = 0x0007; // Core 4nd Gen (HSW)
600 break;
601 }
602 break;
603 case 0xD:
604 imc_type = 0x0005; // SNB-E
605 break;
606 case 0xE:
607 imc_type = 0x0001; // Core i7 1st Gen 45 nm (NHM)
608 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800609 }
Ben Gardner90f7d112016-03-15 15:25:22 -0500610
611 if (imc_type) { tsc_invariable = 1; }
612 return;
613 }
Martin Roth9b1b3352016-02-24 12:27:06 -0800614}
615
616void smp_default_mode(void)
617{
618 int i, result;
619 char *cpupsn = cpu_id.brand_id.char_array;
Ben Gardner90f7d112016-03-15 15:25:22 -0500620 char *disabledcpu[] = { "Opteron", "Xeon", "Genuine Intel" };
Martin Roth4dcd13d2016-02-24 13:53:07 -0800621
Ben Gardner90f7d112016-03-15 15:25:22 -0500622 for (i = 0; i < 3; i++)
623 {
624 result = strstr(cpupsn, disabledcpu[i]);
625 if (result != -1) { v->fail_safe |= 0b10; }
626 }
Martin Roth4dcd13d2016-02-24 13:53:07 -0800627
Ben Gardner90f7d112016-03-15 15:25:22 -0500628 // For 5.01 release, SMP disabled by defualt by config.h toggle
629 if (CONSERVATIVE_SMP) { v->fail_safe |= 0b10; }
Martin Roth9b1b3352016-02-24 12:27:06 -0800630}
631
632/*
633 * Find CPU type
634 */
635void cpu_type(void)
636{
637 /* If we can get a brand string use it, and we are done */
638 if (cpu_id.max_xcpuid >= 0x80000004) {
639 cprint(0, COL_MID, cpu_id.brand_id.char_array);
640 //If we have a brand string, maybe we have an IMC. Check that.
641 detect_imc();
Martin Roth4dcd13d2016-02-24 13:53:07 -0800642 smp_default_mode();
Martin Roth9b1b3352016-02-24 12:27:06 -0800643 return;
644 }
645
Martin Roth4dcd13d2016-02-24 13:53:07 -0800646 /* The brand string is not available so we need to figure out
Martin Roth9b1b3352016-02-24 12:27:06 -0800647 * CPU what we have */
Ben Gardner90f7d112016-03-15 15:25:22 -0500648 switch (cpu_id.vend_id.char_array[0]) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800649 /* AMD Processors */
650 case 'A':
Ben Gardner90f7d112016-03-15 15:25:22 -0500651 switch (cpu_id.vers.bits.family) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800652 case 4:
Ben Gardner90f7d112016-03-15 15:25:22 -0500653 switch (cpu_id.vers.bits.model) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800654 case 3:
655 cprint(0, COL_MID, "AMD 486DX2");
656 break;
657 case 7:
658 cprint(0, COL_MID, "AMD 486DX2-WB");
659 break;
660 case 8:
661 cprint(0, COL_MID, "AMD 486DX4");
662 break;
663 case 9:
664 cprint(0, COL_MID, "AMD 486DX4-WB");
665 break;
666 case 14:
667 cprint(0, COL_MID, "AMD 5x86-WT");
668 break;
669 case 15:
670 cprint(0, COL_MID, "AMD 5x86-WB");
671 break;
672 }
673 /* Since we can't get CPU speed or cache info return */
674 return;
675 case 5:
Ben Gardner90f7d112016-03-15 15:25:22 -0500676 switch (cpu_id.vers.bits.model) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800677 case 0:
678 case 1:
679 case 2:
680 case 3:
681 cprint(0, COL_MID, "AMD K5");
682 l1_cache = 8;
683 break;
684 case 6:
685 case 7:
686 cprint(0, COL_MID, "AMD K6");
687 break;
688 case 8:
689 cprint(0, COL_MID, "AMD K6-2");
690 break;
691 case 9:
692 cprint(0, COL_MID, "AMD K6-III");
693 break;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800694 case 13:
695 cprint(0, COL_MID, "AMD K6-III+");
Martin Roth9b1b3352016-02-24 12:27:06 -0800696 break;
697 }
698 break;
699 case 6:
700
Ben Gardner90f7d112016-03-15 15:25:22 -0500701 switch (cpu_id.vers.bits.model) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800702 case 1:
703 cprint(0, COL_MID, "AMD Athlon (0.25)");
704 break;
705 case 2:
706 case 4:
707 cprint(0, COL_MID, "AMD Athlon (0.18)");
708 break;
709 case 6:
710 if (l2_cache == 64) {
711 cprint(0, COL_MID, "AMD Duron (0.18)");
712 } else {
713 cprint(0, COL_MID, "Athlon XP (0.18)");
714 }
715 break;
716 case 8:
717 case 10:
718 if (l2_cache == 64) {
719 cprint(0, COL_MID, "AMD Duron (0.13)");
720 } else {
721 cprint(0, COL_MID, "Athlon XP (0.13)");
722 }
723 break;
724 case 3:
725 case 7:
726 cprint(0, COL_MID, "AMD Duron");
727 /* Duron stepping 0 CPUID for L2 is broken */
728 /* (AMD errata T13)*/
729 if (cpu_id.vers.bits.stepping == 0) { /* stepping 0 */
730 /* Hard code the right L2 size */
731 l2_cache = 64;
732 } else {
733 }
734 break;
735 }
736 break;
737
738 /* All AMD family values >= 10 have the Brand ID
739 * feature so we don't need to find the CPU type */
740 }
741 break;
742
743 /* Intel or Transmeta Processors */
744 case 'G':
Ben Gardner90f7d112016-03-15 15:25:22 -0500745 if (cpu_id.vend_id.char_array[7] == 'T') { /* GenuineTMx86 */
Martin Roth9b1b3352016-02-24 12:27:06 -0800746 if (cpu_id.vers.bits.family == 5) {
747 cprint(0, COL_MID, "TM 5x00");
748 } else if (cpu_id.vers.bits.family == 15) {
749 cprint(0, COL_MID, "TM 8x00");
750 }
751 l1_cache = cpu_id.cache_info.ch[3] + cpu_id.cache_info.ch[7];
752 l2_cache = (cpu_id.cache_info.ch[11]*256) + cpu_id.cache_info.ch[10];
Ben Gardner90f7d112016-03-15 15:25:22 -0500753 } else { /* GenuineIntel */
Martin Roth9b1b3352016-02-24 12:27:06 -0800754 if (cpu_id.vers.bits.family == 4) {
Ben Gardner90f7d112016-03-15 15:25:22 -0500755 switch (cpu_id.vers.bits.model) {
756 case 0:
757 case 1:
758 cprint(0, COL_MID, "Intel 486DX");
759 break;
760 case 2:
761 cprint(0, COL_MID, "Intel 486SX");
762 break;
763 case 3:
764 cprint(0, COL_MID, "Intel 486DX2");
765 break;
766 case 4:
767 cprint(0, COL_MID, "Intel 486SL");
768 break;
769 case 5:
770 cprint(0, COL_MID, "Intel 486SX2");
771 break;
772 case 7:
773 cprint(0, COL_MID, "Intel 486DX2-WB");
774 break;
775 case 8:
776 cprint(0, COL_MID, "Intel 486DX4");
777 break;
778 case 9:
779 cprint(0, COL_MID, "Intel 486DX4-WB");
780 break;
781 }
782 /* Since we can't get CPU speed or cache info return */
783 return;
Martin Roth9b1b3352016-02-24 12:27:06 -0800784 }
Martin Roth9b1b3352016-02-24 12:27:06 -0800785
786
Ben Gardner90f7d112016-03-15 15:25:22 -0500787 switch (cpu_id.vers.bits.family) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800788 case 5:
Ben Gardner90f7d112016-03-15 15:25:22 -0500789 switch (cpu_id.vers.bits.model) {
790 case 0:
791 case 1:
792 case 2:
793 case 3:
794 case 7:
795 cprint(0, COL_MID, "Pentium");
796 if (l1_cache == 0) {
797 l1_cache = 8;
798 }
799 break;
800 case 4:
801 case 8:
802 cprint(0, COL_MID, "Pentium-MMX");
803 if (l1_cache == 0) {
804 l1_cache = 16;
805 }
806 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800807 }
808 break;
809 case 6:
Ben Gardner90f7d112016-03-15 15:25:22 -0500810 switch (cpu_id.vers.bits.model) {
811 case 0:
812 case 1:
813 cprint(0, COL_MID, "Pentium Pro");
814 break;
815 case 3:
816 case 4:
Martin Roth9b1b3352016-02-24 12:27:06 -0800817 cprint(0, COL_MID, "Pentium II");
Ben Gardner90f7d112016-03-15 15:25:22 -0500818 break;
819 case 5:
820 if (l2_cache == 0) {
821 cprint(0, COL_MID, "Celeron");
822 } else {
823 cprint(0, COL_MID, "Pentium II");
824 }
825 break;
826 case 6:
827 if (l2_cache == 128) {
828 cprint(0, COL_MID, "Celeron");
829 } else {
830 cprint(0, COL_MID, "Pentium II");
831 }
Martin Roth9b1b3352016-02-24 12:27:06 -0800832 }
833 break;
834 case 7:
835 case 8:
836 case 11:
837 if (l2_cache == 128) {
838 cprint(0, COL_MID, "Celeron");
839 } else {
840 cprint(0, COL_MID, "Pentium III");
841 }
842 break;
843 case 9:
844 if (l2_cache == 512) {
845 cprint(0, COL_MID, "Celeron M (0.13)");
846 } else {
847 cprint(0, COL_MID, "Pentium M (0.13)");
848 }
849 break;
Ben Gardner90f7d112016-03-15 15:25:22 -0500850 case 10:
Martin Roth9b1b3352016-02-24 12:27:06 -0800851 cprint(0, COL_MID, "Pentium III Xeon");
852 break;
853 case 12:
854 l1_cache = 24;
855 cprint(0, COL_MID, "Atom (0.045)");
Martin Roth4dcd13d2016-02-24 13:53:07 -0800856 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800857 case 13:
858 if (l2_cache == 1024) {
859 cprint(0, COL_MID, "Celeron M (0.09)");
860 } else {
861 cprint(0, COL_MID, "Pentium M (0.09)");
862 }
863 break;
864 case 14:
865 cprint(0, COL_MID, "Intel Core");
Martin Roth4dcd13d2016-02-24 13:53:07 -0800866 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800867 case 15:
868 if (l2_cache == 1024) {
869 cprint(0, COL_MID, "Pentium E");
870 } else {
871 cprint(0, COL_MID, "Intel Core 2");
872 }
873 break;
874 }
875 break;
876 case 15:
Ben Gardner90f7d112016-03-15 15:25:22 -0500877 switch (cpu_id.vers.bits.model) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800878 case 0:
Martin Roth4dcd13d2016-02-24 13:53:07 -0800879 case 1:
Martin Roth9b1b3352016-02-24 12:27:06 -0800880 case 2:
881 if (l2_cache == 128) {
882 cprint(0, COL_MID, "Celeron");
883 } else {
884 cprint(0, COL_MID, "Pentium 4");
885 }
886 break;
887 case 3:
888 case 4:
889 if (l2_cache == 256) {
890 cprint(0, COL_MID, "Celeron (0.09)");
891 } else {
892 cprint(0, COL_MID, "Pentium 4 (0.09)");
893 }
894 break;
895 case 6:
896 cprint(0, COL_MID, "Pentium D (65nm)");
897 break;
898 default:
899 cprint(0, COL_MID, "Unknown Intel");
Ben Gardner90f7d112016-03-15 15:25:22 -0500900 break;
901 }
Martin Roth9b1b3352016-02-24 12:27:06 -0800902 }
903 break;
904
905 /* VIA/Cyrix/Centaur Processors with CPUID */
906 case 'C':
Ben Gardner90f7d112016-03-15 15:25:22 -0500907 if (cpu_id.vend_id.char_array[1] == 'e') { /* CentaurHauls */
Martin Roth9b1b3352016-02-24 12:27:06 -0800908 l1_cache = cpu_id.cache_info.ch[3] + cpu_id.cache_info.ch[7];
909 l2_cache = cpu_id.cache_info.ch[11];
Ben Gardner90f7d112016-03-15 15:25:22 -0500910 switch (cpu_id.vers.bits.family) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800911 case 5:
912 cprint(0, COL_MID, "Centaur 5x86");
913 break;
914 case 6: // VIA C3
Ben Gardner90f7d112016-03-15 15:25:22 -0500915 switch (cpu_id.vers.bits.model) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800916 default:
Ben Gardner90f7d112016-03-15 15:25:22 -0500917 if (cpu_id.vers.bits.stepping < 8) {
918 cprint(0, COL_MID, "VIA C3 Samuel2");
919 } else {
920 cprint(0, COL_MID, "VIA C3 Eden");
921 }
922 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800923 case 10:
924 cprint(0, COL_MID, "VIA C7 (C5J)");
925 l1_cache = 64;
926 l2_cache = 128;
927 break;
928 case 13:
929 cprint(0, COL_MID, "VIA C7 (C5R)");
930 l1_cache = 64;
931 l2_cache = 128;
932 break;
933 case 15:
934 cprint(0, COL_MID, "VIA Isaiah (CN)");
935 l1_cache = 64;
936 l2_cache = 128;
937 break;
938 }
939 }
Ben Gardner90f7d112016-03-15 15:25:22 -0500940 } else { /* CyrixInstead */
941 switch (cpu_id.vers.bits.family) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800942 case 5:
Ben Gardner90f7d112016-03-15 15:25:22 -0500943 switch (cpu_id.vers.bits.model) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800944 case 0:
945 cprint(0, COL_MID, "Cyrix 6x86MX/MII");
946 break;
947 case 4:
948 cprint(0, COL_MID, "Cyrix GXm");
949 break;
950 }
951 return;
952
953 case 6: // VIA C3
Ben Gardner90f7d112016-03-15 15:25:22 -0500954 switch (cpu_id.vers.bits.model) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800955 case 6:
956 cprint(0, COL_MID, "Cyrix III");
957 break;
958 case 7:
959 if (cpu_id.vers.bits.stepping < 8) {
960 cprint(0, COL_MID, "VIA C3 Samuel2");
961 } else {
962 cprint(0, COL_MID, "VIA C3 Ezra-T");
963 }
964 break;
965 case 8:
966 cprint(0, COL_MID, "VIA C3 Ezra-T");
967 break;
968 case 9:
969 cprint(0, COL_MID, "VIA C3 Nehemiah");
970 break;
971 }
972 // L1 = L2 = 64 KB from Cyrix III to Nehemiah
973 l1_cache = 64;
974 l2_cache = 64;
975 break;
976 }
977 }
978 break;
979 /* Unknown processor */
980 default:
981 /* Make a guess at the family */
Ben Gardner90f7d112016-03-15 15:25:22 -0500982 switch (cpu_id.vers.bits.family) {
Martin Roth9b1b3352016-02-24 12:27:06 -0800983 case 5:
984 cprint(0, COL_MID, "586");
Ben Gardner88de1af2016-03-30 15:09:26 -0500985 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800986 case 6:
987 cprint(0, COL_MID, "686");
Ben Gardner88de1af2016-03-30 15:09:26 -0500988 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800989 default:
990 cprint(0, COL_MID, "Unidentified Processor");
991 }
992 }
993}
994
Ben Gardner90f7d112016-03-15 15:25:22 -0500995#define STEST_ADDR 0x100000 /* Measure memory speed starting at 1MB */
Martin Roth9b1b3352016-02-24 12:27:06 -0800996
997/* Measure and display CPU and cache sizes and speeds */
Martin Roth69593432016-03-27 19:46:03 -0600998void cpu_cache_speed(void)
Martin Roth9b1b3352016-02-24 12:27:06 -0800999{
1000 int i, off = 4;
1001 ulong speed;
1002
1003
1004 /* Print CPU speed */
1005 if ((speed = cpuspeed()) > 0) {
1006 if (speed < 999499) {
1007 speed += 50; /* for rounding */
1008 cprint(1, off, " . MHz");
1009 dprint(1, off+1, speed/1000, 3, 1);
1010 dprint(1, off+5, (speed/100)%10, 1, 0);
1011 } else {
1012 speed += 500; /* for rounding */
1013 cprint(1, off, " MHz");
1014 dprint(1, off, speed/1000, 5, 0);
1015 }
1016 extclock = speed;
1017 }
1018
1019 /* Print out L1 cache info */
1020 /* To measure L1 cache speed we use a block size that is 1/4th */
1021 /* of the total L1 cache size since half of it is for instructions */
1022 if (l1_cache) {
1023 cprint(2, 0, "L1 Cache: K ");
1024 dprint(2, 11, l1_cache, 3, 0);
1025 if ((speed=memspeed(STEST_ADDR, (l1_cache/2)*1024, 200))) {
1026 cprint(2, 16, " MB/s");
1027 dprint(2, 16, speed, 6, 0);
1028 }
1029 }
1030
1031 /* Print out L2 cache info */
1032 /* We measure the L2 cache speed by using a block size that is */
1033 /* the size of the L1 cache. We have to fudge if the L1 */
1034 /* cache is bigger than the L2 */
1035 if (l2_cache) {
1036 cprint(3, 0, "L2 Cache: K ");
1037 dprint(3, 10, l2_cache, 4, 0);
1038
1039 if (l2_cache < l1_cache) {
1040 i = l1_cache / 4 + l2_cache / 4;
1041 } else {
1042 i = l1_cache;
1043 }
1044 if ((speed=memspeed(STEST_ADDR, i*1024, 200))) {
1045 cprint(3, 16, " MB/s");
1046 dprint(3, 16, speed, 6, 0);
1047 }
1048 }
1049 /* Print out L3 cache info */
1050 /* We measure the L3 cache speed by using a block size that is */
1051 /* 2X the size of the L2 cache. */
1052
Ben Gardner90f7d112016-03-15 15:25:22 -05001053 if (l3_cache) {
Martin Roth9b1b3352016-02-24 12:27:06 -08001054 cprint(4, 0, "L3 Cache: K ");
Ben Gardner90f7d112016-03-15 15:25:22 -05001055 aprint(4, 10, l3_cache/4);
1056 //dprint(4, 10, l3_cache, 4, 0);
Martin Roth4dcd13d2016-02-24 13:53:07 -08001057
Ben Gardner90f7d112016-03-15 15:25:22 -05001058 i = l2_cache*2;
Martin Roth4dcd13d2016-02-24 13:53:07 -08001059
Ben Gardner90f7d112016-03-15 15:25:22 -05001060 if ((speed=memspeed(STEST_ADDR, i*1024, 150))) {
1061 cprint(4, 16, " MB/s");
1062 dprint(4, 16, speed, 6, 0);
1063 }
1064 }
Martin Roth9b1b3352016-02-24 12:27:06 -08001065}
1066
1067/* Measure and display memory speed, multitasked using all CPUs */
1068ulong spd[MAX_CPUS];
1069void get_mem_speed(int me, int ncpus)
1070{
1071 int i;
1072 ulong speed=0;
1073
Ben Gardner90f7d112016-03-15 15:25:22 -05001074 /* Determine memory speed. To find the memory speed we use
1075 * A block size that is the sum of all the L1, L2 & L3 caches
Martin Roth9b1b3352016-02-24 12:27:06 -08001076 * in all cpus * 6 */
Ben Gardner90f7d112016-03-15 15:25:22 -05001077 i = (l3_cache + l2_cache + l1_cache) * 4;
Martin Roth9b1b3352016-02-24 12:27:06 -08001078
1079 /* Make sure that we have enough memory to do the test */
1080 /* If not use all we have */
1081 if ((1 + (i * 2)) > (v->plim_upper << 2)) {
1082 i = ((v->plim_upper <<2) - 1) / 2;
1083 }
Martin Roth4dcd13d2016-02-24 13:53:07 -08001084
Martin Roth9b1b3352016-02-24 12:27:06 -08001085 speed = memspeed(STEST_ADDR, i * 1024, 100);
1086 cprint(5, 16, " MB/s");
1087 dprint(5, 16, speed, 6, 0);
Martin Roth9b1b3352016-02-24 12:27:06 -08001088}
1089
1090/* #define TICKS 5 * 11832 (count = 6376)*/
1091/* #define TICKS (65536 - 12752) */
Ben Gardner90f7d112016-03-15 15:25:22 -05001092#define TICKS 59659 /* 50 ms */
Martin Roth9b1b3352016-02-24 12:27:06 -08001093
1094/* Returns CPU clock in khz */
1095ulong stlow, sthigh;
1096static int cpuspeed(void)
1097{
1098 int loops;
1099 ulong end_low, end_high;
1100
Ben Gardner90f7d112016-03-15 15:25:22 -05001101 if (cpu_id.fid.bits.rdtsc == 0) {
Martin Roth9b1b3352016-02-24 12:27:06 -08001102 return(-1);
1103 }
1104
1105 /* Setup timer */
1106 outb((inb(0x61) & ~0x02) | 0x01, 0x61);
Martin Roth4dcd13d2016-02-24 13:53:07 -08001107 outb(0xb0, 0x43);
Martin Roth9b1b3352016-02-24 12:27:06 -08001108 outb(TICKS & 0xff, 0x42);
1109 outb(TICKS >> 8, 0x42);
1110
Ben Gardner90f7d112016-03-15 15:25:22 -05001111 asm __volatile__ ("rdtsc" : "=a" (stlow), "=d" (sthigh));
Martin Roth9b1b3352016-02-24 12:27:06 -08001112
1113 loops = 0;
1114 do {
1115 loops++;
1116 } while ((inb(0x61) & 0x20) == 0);
1117
1118 asm __volatile__ (
1119 "rdtsc\n\t" \
1120 "subl stlow,%%eax\n\t" \
1121 "sbbl sthigh,%%edx\n\t" \
Ben Gardner90f7d112016-03-15 15:25:22 -05001122 : "=a" (end_low), "=d" (end_high)
Martin Roth9b1b3352016-02-24 12:27:06 -08001123 );
1124
1125 /* Make sure we have a credible result */
1126 if (loops < 4 || end_low < 50000) {
1127 return(-1);
1128 }
1129 v->clks_msec = end_low/50;
1130
1131 if (tsc_invariable) end_low = correct_tsc(end_low);
1132
1133 return(v->clks_msec);
1134}
1135
1136/* Measure cache speed by copying a block of memory. */
1137/* Returned value is kbytes/second */
1138ulong memspeed(ulong src, ulong len, int iter)
1139{
1140 int i;
1141 ulong dst, wlen;
1142 ulong st_low, st_high;
1143 ulong end_low, end_high;
1144 ulong cal_low, cal_high;
1145
Ben Gardner90f7d112016-03-15 15:25:22 -05001146 if (cpu_id.fid.bits.rdtsc == 0) {
Martin Roth9b1b3352016-02-24 12:27:06 -08001147 return(-1);
1148 }
1149 if (len == 0) return(-2);
1150
1151 dst = src + len;
1152 wlen = len / 4; /* Length is bytes */
1153
1154 /* Calibrate the overhead with a zero word copy */
Ben Gardner90f7d112016-03-15 15:25:22 -05001155 asm __volatile__ ("rdtsc" : "=a" (st_low), "=d" (st_high));
Martin Roth9b1b3352016-02-24 12:27:06 -08001156 for (i=0; i<iter; i++) {
1157 asm __volatile__ (
1158 "movl %0,%%esi\n\t" \
Ben Gardner90f7d112016-03-15 15:25:22 -05001159 "movl %1,%%edi\n\t" \
1160 "movl %2,%%ecx\n\t" \
1161 "cld\n\t" \
1162 "rep\n\t" \
1163 "movsl\n\t" \
1164 : : "g" (src), "g" (dst), "g" (0)
Martin Roth9b1b3352016-02-24 12:27:06 -08001165 : "esi", "edi", "ecx"
1166 );
1167 }
Ben Gardner90f7d112016-03-15 15:25:22 -05001168 asm __volatile__ ("rdtsc" : "=a" (cal_low), "=d" (cal_high));
Martin Roth9b1b3352016-02-24 12:27:06 -08001169
1170 /* Compute the overhead time */
1171 asm __volatile__ (
1172 "subl %2,%0\n\t"
1173 "sbbl %3,%1"
Ben Gardner90f7d112016-03-15 15:25:22 -05001174 : "=a" (cal_low), "=d" (cal_high)
1175 : "g" (st_low), "g" (st_high),
1176 "0" (cal_low), "1" (cal_high)
Martin Roth9b1b3352016-02-24 12:27:06 -08001177 );
1178
1179
1180 /* Now measure the speed */
1181 /* Do the first copy to prime the cache */
1182 asm __volatile__ (
1183 "movl %0,%%esi\n\t" \
1184 "movl %1,%%edi\n\t" \
Ben Gardner90f7d112016-03-15 15:25:22 -05001185 "movl %2,%%ecx\n\t" \
1186 "cld\n\t" \
1187 "rep\n\t" \
1188 "movsl\n\t" \
1189 : : "g" (src), "g" (dst), "g" (wlen)
Martin Roth9b1b3352016-02-24 12:27:06 -08001190 : "esi", "edi", "ecx"
1191 );
Ben Gardner90f7d112016-03-15 15:25:22 -05001192 asm __volatile__ ("rdtsc" : "=a" (st_low), "=d" (st_high));
Martin Roth9b1b3352016-02-24 12:27:06 -08001193 for (i=0; i<iter; i++) {
Ben Gardner90f7d112016-03-15 15:25:22 -05001194 asm __volatile__ (
Martin Roth9b1b3352016-02-24 12:27:06 -08001195 "movl %0,%%esi\n\t" \
1196 "movl %1,%%edi\n\t" \
Ben Gardner90f7d112016-03-15 15:25:22 -05001197 "movl %2,%%ecx\n\t" \
1198 "cld\n\t" \
1199 "rep\n\t" \
1200 "movsl\n\t" \
1201 : : "g" (src), "g" (dst), "g" (wlen)
Martin Roth9b1b3352016-02-24 12:27:06 -08001202 : "esi", "edi", "ecx"
1203 );
1204 }
Ben Gardner90f7d112016-03-15 15:25:22 -05001205 asm __volatile__ ("rdtsc" : "=a" (end_low), "=d" (end_high));
Martin Roth9b1b3352016-02-24 12:27:06 -08001206
1207 /* Compute the elapsed time */
1208 asm __volatile__ (
1209 "subl %2,%0\n\t"
1210 "sbbl %3,%1"
Ben Gardner90f7d112016-03-15 15:25:22 -05001211 : "=a" (end_low), "=d" (end_high)
1212 : "g" (st_low), "g" (st_high),
1213 "0" (end_low), "1" (end_high)
Martin Roth9b1b3352016-02-24 12:27:06 -08001214 );
1215 /* Subtract the overhead time */
1216 asm __volatile__ (
1217 "subl %2,%0\n\t"
1218 "sbbl %3,%1"
Ben Gardner90f7d112016-03-15 15:25:22 -05001219 : "=a" (end_low), "=d" (end_high)
1220 : "g" (cal_low), "g" (cal_high),
1221 "0" (end_low), "1" (end_high)
Martin Roth9b1b3352016-02-24 12:27:06 -08001222 );
1223
1224 /* Make sure that the result fits in 32 bits */
1225 //hprint(11,40,end_high);
1226 if (end_high) {
1227 return(-3);
1228 }
1229 end_low /= 2;
1230
1231 /* Convert to clocks/KB */
1232 end_low /= len;
1233 end_low *= 1024;
1234 end_low /= iter;
1235 if (end_low == 0) {
1236 return(-4);
1237 }
1238
1239 /* Convert to kbytes/sec */
1240
1241 if (tsc_invariable) end_low = correct_tsc(end_low);
1242
1243 return((v->clks_msec)/end_low);
1244}
1245
Ben Gardner90f7d112016-03-15 15:25:22 -05001246#define rdmsr(msr, val1, val2) \
1247 __asm__ __volatile__ ("rdmsr" \
1248 : "=a" (val1), "=d" (val2) \
1249 : "c" (msr))
Martin Roth9b1b3352016-02-24 12:27:06 -08001250
1251
1252ulong correct_tsc(ulong el_org)
1253{
1254 float coef_now, coef_max;
1255 int msr_lo, msr_hi, is_xe;
Martin Roth4dcd13d2016-02-24 13:53:07 -08001256
Martin Roth9b1b3352016-02-24 12:27:06 -08001257 rdmsr(0x198, msr_lo, msr_hi);
Martin Roth4dcd13d2016-02-24 13:53:07 -08001258 is_xe = (msr_lo >> 31) & 0x1;
1259
Ben Gardner90f7d112016-03-15 15:25:22 -05001260 if (is_xe) {
Martin Roth9b1b3352016-02-24 12:27:06 -08001261 rdmsr(0x198, msr_lo, msr_hi);
Martin Roth4dcd13d2016-02-24 13:53:07 -08001262 coef_max = ((msr_hi >> 8) & 0x1F);
Martin Roth9b1b3352016-02-24 12:27:06 -08001263 if ((msr_hi >> 14) & 0x1) { coef_max = coef_max + 0.5f; }
1264 } else {
1265 rdmsr(0x17, msr_lo, msr_hi);
1266 coef_max = ((msr_lo >> 8) & 0x1F);
1267 if ((msr_lo >> 14) & 0x1) { coef_max = coef_max + 0.5f; }
1268 }
Martin Roth4dcd13d2016-02-24 13:53:07 -08001269
Ben Gardner90f7d112016-03-15 15:25:22 -05001270 if (cpu_id.fid.bits.eist) {
Martin Roth9b1b3352016-02-24 12:27:06 -08001271 rdmsr(0x198, msr_lo, msr_hi);
1272 coef_now = ((msr_lo >> 8) & 0x1F);
1273 if ((msr_lo >> 14) & 0x1) { coef_now = coef_now + 0.5f; }
1274 } else {
1275 rdmsr(0x2A, msr_lo, msr_hi);
1276 coef_now = (msr_lo >> 22) & 0x1F;
1277 }
Ben Gardner90f7d112016-03-15 15:25:22 -05001278 if (coef_max && coef_now) {
Martin Roth9b1b3352016-02-24 12:27:06 -08001279 el_org = (ulong)(el_org * coef_now / coef_max);
1280 }
1281 return el_org;
1282}