/* * MemTest86+ V5 Specific code (GPL V2.0) * By Samuel DEMEULEMEESTER, sdemeule@memtest.org * http://www.canardpc.com - http://www.memtest.org * ------------------------------------------------ * init.c - MemTest-86 Version 3.6 * * Released under version 2 of the Gnu Public License. * By Chris Brady */ #include "stdin.h" #include "stddef.h" #include "test.h" #include "defs.h" #include "config.h" #include "cpuid.h" #include "smp.h" #include "io.h" #include "spd.h" #include "pci.h" #include "controller.h" extern struct tseq tseq[]; extern short memsz_mode; extern int num_cpus; extern int act_cpus; extern int found_cpus; unsigned long imc_type = 0; extern int maxcpus; extern char cpu_mask[]; extern void initialise_cpus(); /* Here we store all of the cpuid data */ extern struct cpu_ident cpu_id; int l1_cache=0, l2_cache=0, l3_cache=0; int tsc_invariable = 0; ulong extclock; ulong memspeed(ulong src, ulong len, int iter); static void cpu_type(void); static int cpuspeed(void); static void get_cache_size(); static void cpu_cache_speed(); void get_cpuid(); int beepmode; int conservative_smp = CONSERVATIVE_SMP; extern short dmi_initialized; extern int dmi_err_cnts[MAX_DMI_MEMDEVS]; /* Failsafe function */ /* msec: number of ms to wait - scs: scancode expected to stop */ /* bits: 0 = extended detection - 1: SMP - 2: Temp Check */ /* 3: MP SMP - 4-7: RSVD */ void failsafe(int msec, int scs) { int i; ulong sh, sl, l, h, t; unsigned char c; volatile char *pp; for(i=0, pp=(char *)(SCREEN_ADR+(18*160)+(18*2)+1); i<40; i++, pp+=2) { *pp = 0x1E; } for(i=0, pp=(char *)(SCREEN_ADR+(18*160)+(18*2)+1); i<3; i++, pp+=2) { *pp = 0x9E; } for(i=0, pp=(char *)(SCREEN_ADR+(18*160)+(55*2)+1); i<3; i++, pp+=2) { *pp = 0x9E; } cprint(18, 18, "==> Press F1 to enter Fail-Safe Mode <=="); if(v->fail_safe & 2) { cprint(19, 15, "==> Press F2 to force Multi-Threading (SMP) <=="); } /* save the starting time */ asm __volatile__( "rdtsc":"=a" (sl),"=d" (sh)); /* loop for n seconds */ while (1) { asm __volatile__( "rdtsc":"=a" (l),"=d" (h)); asm __volatile__ ( "subl %2,%0\n\t" "sbbl %3,%1" :"=a" (l), "=d" (h) :"g" (sl), "g" (sh), "0" (l), "1" (h)); t = h * ((unsigned)0xffffffff / v->clks_msec); t += (l / v->clks_msec); /* Is the time up? */ if (t >= msec) { break; } /* Is expected Scan code pressed? */ c = get_key(); c &= 0x7f; /* F1 */ if(c == scs) { v->fail_safe |= 1; break; } /* F2 */ if(c == scs+1) { v->fail_safe ^= 2; break; } /* F3 */ if(c == scs+2) { if(v->fail_safe & 2) { v->fail_safe ^= 2; } v->fail_safe |= 8; break; } } cprint(18, 18, " "); cprint(19, 15, " "); for(i=0, pp=(char *)(SCREEN_ADR+(18*160)+(18*2)+1); i<40; i++, pp+=2) { *pp = 0x17; } } static void display_init(void) { int i; volatile char *pp; /* Set HW cursor out of screen boundaries */ __outb(0x0F, 0x03D4); __outb(0xFF, 0x03D5); __outb(0x0E, 0x03D4); __outb(0xFF, 0x03D5); serial_echo_init(); serial_echo_print("INE_SCROLL;24r"); /* Set scroll area row 7-23 */ serial_echo_print(""); /* Clear Screen */ serial_echo_print(""); serial_echo_print(""); serial_echo_print(""); /* Clear screen & set background to blue */ for(i=0, pp=(char *)(SCREEN_ADR); i<80*24; i++) { *pp++ = ' '; *pp++ = 0x17; } /* Make the name background green */ for(i=0, pp=(char *)(SCREEN_ADR+1); itest_pages); v->pass = 0; v->msg_line = 0; v->ecount = 0; v->ecc_ecount = 0; v->testsel = -1; v->msg_line = LINE_SCROLL-1; v->scroll_start = v->msg_line * 160; v->erri.low_addr.page = 0x7fffffff; v->erri.low_addr.offset = 0xfff; v->erri.high_addr.page = 0; v->erri.high_addr.offset = 0; v->erri.min_bits = 32; v->erri.max_bits = 0; v->erri.min_bits = 32; v->erri.max_bits = 0; v->erri.maxl = 0; v->erri.cor_err = 0; v->erri.ebits = 0; v->erri.hdr_flag = 0; v->erri.tbits = 0; for (i=0; tseq[i].msg != NULL; i++) { tseq[i].errors = 0; } if (dmi_initialized) { for (i=0; i < MAX_DMI_MEMDEVS; i++){ if (dmi_err_cnts[i] > 0) { dmi_err_cnts[i] = 0; } } } /* setup beep mode */ beepmode = BEEP_MODE; /* Get the cpu and cache information */ get_cpuid(); /* setup pci */ pci_init(); get_cache_size(); cpu_type(); cpu_cache_speed(); /* Check fail safe */ failsafe(5000, 0x3B); /* Initalize SMP */ initialise_cpus(); for (i = 0; i fail_safe & 3) == 2) { cprint(LINE_CPU,9, "(SMP: Disabled)"); cprint(LINE_RAM,9, "Running..."); } // dprint(10, 5, found_cpus, 2, 0); /* Find Memory Specs */ if(v->fail_safe & 1) { cprint(LINE_CPU, COL_SPEC, " **** FAIL SAFE **** FAIL SAFE **** "); cprint(LINE_RAM, COL_SPEC, " No detection, same reliability "); } else { find_controller(); get_spd_spec(); if(num_cpus <= 16 && !(v->fail_safe & 4)) { coretemp(); } } if(v->check_temp > 0 && !(v->fail_safe & 4)) { cprint(LINE_CPU, 26, "| CPU Temp"); cprint(LINE_CPU+1, 26, "| øC"); } beep(600); beep(1000); /* Record the start time */ asm __volatile__ ("rdtsc":"=a" (v->startl),"=d" (v->starth)); v->snapl = v->startl; v->snaph = v->starth; if (l1_cache == 0) { l1_cache = 64; } if (l2_cache == 0) { l1_cache = 512; } v->printmode=PRINTMODE_ADDRESSES; v->numpatn=0; } /* Get cache sizes for most AMD and Intel CPUs, exceptions for old CPUs are * handled in CPU detection */ void get_cache_size() { int i, j, n, size; unsigned int v[4]; unsigned char *dp = (unsigned char *)v; struct cpuid4_eax *eax = (struct cpuid4_eax *)&v[0]; struct cpuid4_ebx *ebx = (struct cpuid4_ebx *)&v[1]; struct cpuid4_ecx *ecx = (struct cpuid4_ecx *)&v[2]; switch(cpu_id.vend_id.char_array[0]) { /* AMD Processors */ case 'A': //l1_cache = cpu_id.cache_info.amd.l1_i_sz; l1_cache = cpu_id.cache_info.amd.l1_d_sz; l2_cache = cpu_id.cache_info.amd.l2_sz; l3_cache = cpu_id.cache_info.amd.l3_sz; l3_cache *= 512; break; case 'G': /* Intel Processors */ l1_cache = 0; l2_cache = 0; l3_cache = 0; /* Use CPUID(4) if it is available */ if (cpu_id.max_cpuid > 3) { /* figure out how many cache leaves */ n = -1; do { ++n; /* Do cpuid(4) loop to find out num_cache_leaves */ cpuid_count(4, n, &v[0], &v[1], &v[2], &v[3]); } while ((eax->ctype) != 0); /* loop through all of the leaves */ for (i=0; ictype == 1 || eax->ctype == 3) { /* Compute the cache size */ size = (ecx->number_of_sets + 1) * (ebx->coherency_line_size + 1) * (ebx->physical_line_partition + 1) * (ebx->ways_of_associativity + 1); size /= 1024; switch (eax->level) { case 1: l1_cache += size; break; case 2: l2_cache += size; break; case 3: l3_cache += size; break; } } } return; } /* No CPUID(4) so we use the older CPUID(2) method */ /* Get number of times to iterate */ cpuid(2, &v[0], &v[1], &v[2], &v[3]); n = v[0] & 0xff; for (i=0 ; ifail_safe |= 4; } // Atom Clover Trail if(cpu_id.vers.bits.extendedModel == 4) { imc_type = 0x0007; } // HSW-ULT break; case 0x6: if(cpu_id.vers.bits.extendedModel == 3) { imc_type = 0x0009; // Atom Cedar Trail v->fail_safe |= 4; // Disable Core temp } break; case 0xA: switch(cpu_id.vers.bits.extendedModel) { case 0x1: imc_type = 0x0001; // Core i7 1st Gen 45 nm (NHME) break; case 0x2: imc_type = 0x0004; // Core 2nd Gen (SNB) break; case 0x3: imc_type = 0x0006; // Core 3nd Gen (IVB) break; } break; case 0xC: switch(cpu_id.vers.bits.extendedModel) { case 0x1: if(cpu_id.vers.bits.stepping > 9) { imc_type = 0x0008; } // Atom PineView v->fail_safe |= 4; // Disable Core temp break; case 0x2: imc_type = 0x0002; // Core i7 1st Gen 32 nm (WMR) break; case 0x3: imc_type = 0x0007; // Core 4nd Gen (HSW) break; } break; case 0xD: imc_type = 0x0005; // SNB-E break; case 0xE: imc_type = 0x0001; // Core i7 1st Gen 45 nm (NHM) break; } if(imc_type) { tsc_invariable = 1; } return; } } void smp_default_mode(void) { int i, result; char *cpupsn = cpu_id.brand_id.char_array; char *disabledcpu[] = { "Opteron", "Xeon", "Genuine Intel" }; for(i = 0; i < 3; i++) { result = strstr(cpupsn , disabledcpu[i]); if(result != -1) { v->fail_safe |= 0b10; } } // For 5.01 release, SMP disabled by defualt by config.h toggle if(conservative_smp) { v->fail_safe |= 0b10; } } /* * Find CPU type */ void cpu_type(void) { /* If we can get a brand string use it, and we are done */ if (cpu_id.max_xcpuid >= 0x80000004) { cprint(0, COL_MID, cpu_id.brand_id.char_array); //If we have a brand string, maybe we have an IMC. Check that. detect_imc(); smp_default_mode(); return; } /* The brand string is not available so we need to figure out * CPU what we have */ switch(cpu_id.vend_id.char_array[0]) { /* AMD Processors */ case 'A': switch(cpu_id.vers.bits.family) { case 4: switch(cpu_id.vers.bits.model) { case 3: cprint(0, COL_MID, "AMD 486DX2"); break; case 7: cprint(0, COL_MID, "AMD 486DX2-WB"); break; case 8: cprint(0, COL_MID, "AMD 486DX4"); break; case 9: cprint(0, COL_MID, "AMD 486DX4-WB"); break; case 14: cprint(0, COL_MID, "AMD 5x86-WT"); break; case 15: cprint(0, COL_MID, "AMD 5x86-WB"); break; } /* Since we can't get CPU speed or cache info return */ return; case 5: switch(cpu_id.vers.bits.model) { case 0: case 1: case 2: case 3: cprint(0, COL_MID, "AMD K5"); l1_cache = 8; break; case 6: case 7: cprint(0, COL_MID, "AMD K6"); break; case 8: cprint(0, COL_MID, "AMD K6-2"); break; case 9: cprint(0, COL_MID, "AMD K6-III"); break; case 13: cprint(0, COL_MID, "AMD K6-III+"); break; } break; case 6: switch(cpu_id.vers.bits.model) { case 1: cprint(0, COL_MID, "AMD Athlon (0.25)"); break; case 2: case 4: cprint(0, COL_MID, "AMD Athlon (0.18)"); break; case 6: if (l2_cache == 64) { cprint(0, COL_MID, "AMD Duron (0.18)"); } else { cprint(0, COL_MID, "Athlon XP (0.18)"); } break; case 8: case 10: if (l2_cache == 64) { cprint(0, COL_MID, "AMD Duron (0.13)"); } else { cprint(0, COL_MID, "Athlon XP (0.13)"); } break; case 3: case 7: cprint(0, COL_MID, "AMD Duron"); /* Duron stepping 0 CPUID for L2 is broken */ /* (AMD errata T13)*/ if (cpu_id.vers.bits.stepping == 0) { /* stepping 0 */ /* Hard code the right L2 size */ l2_cache = 64; } else { } break; } break; /* All AMD family values >= 10 have the Brand ID * feature so we don't need to find the CPU type */ } break; /* Intel or Transmeta Processors */ case 'G': if ( cpu_id.vend_id.char_array[7] == 'T' ) { /* GenuineTMx86 */ if (cpu_id.vers.bits.family == 5) { cprint(0, COL_MID, "TM 5x00"); } else if (cpu_id.vers.bits.family == 15) { cprint(0, COL_MID, "TM 8x00"); } l1_cache = cpu_id.cache_info.ch[3] + cpu_id.cache_info.ch[7]; l2_cache = (cpu_id.cache_info.ch[11]*256) + cpu_id.cache_info.ch[10]; } else { /* GenuineIntel */ if (cpu_id.vers.bits.family == 4) { switch(cpu_id.vers.bits.model) { case 0: case 1: cprint(0, COL_MID, "Intel 486DX"); break; case 2: cprint(0, COL_MID, "Intel 486SX"); break; case 3: cprint(0, COL_MID, "Intel 486DX2"); break; case 4: cprint(0, COL_MID, "Intel 486SL"); break; case 5: cprint(0, COL_MID, "Intel 486SX2"); break; case 7: cprint(0, COL_MID, "Intel 486DX2-WB"); break; case 8: cprint(0, COL_MID, "Intel 486DX4"); break; case 9: cprint(0, COL_MID, "Intel 486DX4-WB"); break; } /* Since we can't get CPU speed or cache info return */ return; } switch(cpu_id.vers.bits.family) { case 5: switch(cpu_id.vers.bits.model) { case 0: case 1: case 2: case 3: case 7: cprint(0, COL_MID, "Pentium"); if (l1_cache == 0) { l1_cache = 8; } break; case 4: case 8: cprint(0, COL_MID, "Pentium-MMX"); if (l1_cache == 0) { l1_cache = 16; } break; } break; case 6: switch(cpu_id.vers.bits.model) { case 0: case 1: cprint(0, COL_MID, "Pentium Pro"); break; case 3: case 4: cprint(0, COL_MID, "Pentium II"); break; case 5: if (l2_cache == 0) { cprint(0, COL_MID, "Celeron"); } else { cprint(0, COL_MID, "Pentium II"); } break; case 6: if (l2_cache == 128) { cprint(0, COL_MID, "Celeron"); } else { cprint(0, COL_MID, "Pentium II"); } } break; case 7: case 8: case 11: if (l2_cache == 128) { cprint(0, COL_MID, "Celeron"); } else { cprint(0, COL_MID, "Pentium III"); } break; case 9: if (l2_cache == 512) { cprint(0, COL_MID, "Celeron M (0.13)"); } else { cprint(0, COL_MID, "Pentium M (0.13)"); } break; case 10: cprint(0, COL_MID, "Pentium III Xeon"); break; case 12: l1_cache = 24; cprint(0, COL_MID, "Atom (0.045)"); break; case 13: if (l2_cache == 1024) { cprint(0, COL_MID, "Celeron M (0.09)"); } else { cprint(0, COL_MID, "Pentium M (0.09)"); } break; case 14: cprint(0, COL_MID, "Intel Core"); break; case 15: if (l2_cache == 1024) { cprint(0, COL_MID, "Pentium E"); } else { cprint(0, COL_MID, "Intel Core 2"); } break; } break; case 15: switch(cpu_id.vers.bits.model) { case 0: case 1: case 2: if (l2_cache == 128) { cprint(0, COL_MID, "Celeron"); } else { cprint(0, COL_MID, "Pentium 4"); } break; case 3: case 4: if (l2_cache == 256) { cprint(0, COL_MID, "Celeron (0.09)"); } else { cprint(0, COL_MID, "Pentium 4 (0.09)"); } break; case 6: cprint(0, COL_MID, "Pentium D (65nm)"); break; default: cprint(0, COL_MID, "Unknown Intel"); break; break; } } break; /* VIA/Cyrix/Centaur Processors with CPUID */ case 'C': if ( cpu_id.vend_id.char_array[1] == 'e' ) { /* CentaurHauls */ l1_cache = cpu_id.cache_info.ch[3] + cpu_id.cache_info.ch[7]; l2_cache = cpu_id.cache_info.ch[11]; switch(cpu_id.vers.bits.family){ case 5: cprint(0, COL_MID, "Centaur 5x86"); break; case 6: // VIA C3 switch(cpu_id.vers.bits.model){ default: if (cpu_id.vers.bits.stepping < 8) { cprint(0, COL_MID, "VIA C3 Samuel2"); } else { cprint(0, COL_MID, "VIA C3 Eden"); } break; case 10: cprint(0, COL_MID, "VIA C7 (C5J)"); l1_cache = 64; l2_cache = 128; break; case 13: cprint(0, COL_MID, "VIA C7 (C5R)"); l1_cache = 64; l2_cache = 128; break; case 15: cprint(0, COL_MID, "VIA Isaiah (CN)"); l1_cache = 64; l2_cache = 128; break; } } } else { /* CyrixInstead */ switch(cpu_id.vers.bits.family) { case 5: switch(cpu_id.vers.bits.model) { case 0: cprint(0, COL_MID, "Cyrix 6x86MX/MII"); break; case 4: cprint(0, COL_MID, "Cyrix GXm"); break; } return; case 6: // VIA C3 switch(cpu_id.vers.bits.model) { case 6: cprint(0, COL_MID, "Cyrix III"); break; case 7: if (cpu_id.vers.bits.stepping < 8) { cprint(0, COL_MID, "VIA C3 Samuel2"); } else { cprint(0, COL_MID, "VIA C3 Ezra-T"); } break; case 8: cprint(0, COL_MID, "VIA C3 Ezra-T"); break; case 9: cprint(0, COL_MID, "VIA C3 Nehemiah"); break; } // L1 = L2 = 64 KB from Cyrix III to Nehemiah l1_cache = 64; l2_cache = 64; break; } } break; /* Unknown processor */ default: /* Make a guess at the family */ switch(cpu_id.vers.bits.family) { case 5: cprint(0, COL_MID, "586"); case 6: cprint(0, COL_MID, "686"); default: cprint(0, COL_MID, "Unidentified Processor"); } } } #define STEST_ADDR 0x100000 /* Measure memory speed starting at 1MB */ /* Measure and display CPU and cache sizes and speeds */ void cpu_cache_speed() { int i, off = 4; ulong speed; /* Print CPU speed */ if ((speed = cpuspeed()) > 0) { if (speed < 999499) { speed += 50; /* for rounding */ cprint(1, off, " . MHz"); dprint(1, off+1, speed/1000, 3, 1); dprint(1, off+5, (speed/100)%10, 1, 0); } else { speed += 500; /* for rounding */ cprint(1, off, " MHz"); dprint(1, off, speed/1000, 5, 0); } extclock = speed; } /* Print out L1 cache info */ /* To measure L1 cache speed we use a block size that is 1/4th */ /* of the total L1 cache size since half of it is for instructions */ if (l1_cache) { cprint(2, 0, "L1 Cache: K "); dprint(2, 11, l1_cache, 3, 0); if ((speed=memspeed(STEST_ADDR, (l1_cache/2)*1024, 200))) { cprint(2, 16, " MB/s"); dprint(2, 16, speed, 6, 0); } } /* Print out L2 cache info */ /* We measure the L2 cache speed by using a block size that is */ /* the size of the L1 cache. We have to fudge if the L1 */ /* cache is bigger than the L2 */ if (l2_cache) { cprint(3, 0, "L2 Cache: K "); dprint(3, 10, l2_cache, 4, 0); if (l2_cache < l1_cache) { i = l1_cache / 4 + l2_cache / 4; } else { i = l1_cache; } if ((speed=memspeed(STEST_ADDR, i*1024, 200))) { cprint(3, 16, " MB/s"); dprint(3, 16, speed, 6, 0); } } /* Print out L3 cache info */ /* We measure the L3 cache speed by using a block size that is */ /* 2X the size of the L2 cache. */ if (l3_cache) { cprint(4, 0, "L3 Cache: K "); aprint(4, 10, l3_cache/4); //dprint(4, 10, l3_cache, 4, 0); i = l2_cache*2; if ((speed=memspeed(STEST_ADDR, i*1024, 150))) { cprint(4, 16, " MB/s"); dprint(4, 16, speed, 6, 0); } } } /* Measure and display memory speed, multitasked using all CPUs */ ulong spd[MAX_CPUS]; void get_mem_speed(int me, int ncpus) { int i; ulong speed=0; /* Determine memory speed. To find the memory speed we use * A block size that is the sum of all the L1, L2 & L3 caches * in all cpus * 6 */ i = (l3_cache + l2_cache + l1_cache) * 4; /* Make sure that we have enough memory to do the test */ /* If not use all we have */ if ((1 + (i * 2)) > (v->plim_upper << 2)) { i = ((v->plim_upper <<2) - 1) / 2; } speed = memspeed(STEST_ADDR, i * 1024, 100); cprint(5, 16, " MB/s"); dprint(5, 16, speed, 6, 0); } /* #define TICKS 5 * 11832 (count = 6376)*/ /* #define TICKS (65536 - 12752) */ #define TICKS 59659 /* 50 ms */ /* Returns CPU clock in khz */ ulong stlow, sthigh; static int cpuspeed(void) { int loops; ulong end_low, end_high; if (cpu_id.fid.bits.rdtsc == 0 ) { return(-1); } /* Setup timer */ outb((inb(0x61) & ~0x02) | 0x01, 0x61); outb(0xb0, 0x43); outb(TICKS & 0xff, 0x42); outb(TICKS >> 8, 0x42); asm __volatile__ ("rdtsc":"=a" (stlow),"=d" (sthigh)); loops = 0; do { loops++; } while ((inb(0x61) & 0x20) == 0); asm __volatile__ ( "rdtsc\n\t" \ "subl stlow,%%eax\n\t" \ "sbbl sthigh,%%edx\n\t" \ :"=a" (end_low), "=d" (end_high) ); /* Make sure we have a credible result */ if (loops < 4 || end_low < 50000) { return(-1); } v->clks_msec = end_low/50; if (tsc_invariable) end_low = correct_tsc(end_low); return(v->clks_msec); } /* Measure cache speed by copying a block of memory. */ /* Returned value is kbytes/second */ ulong memspeed(ulong src, ulong len, int iter) { int i; ulong dst, wlen; ulong st_low, st_high; ulong end_low, end_high; ulong cal_low, cal_high; if (cpu_id.fid.bits.rdtsc == 0 ) { return(-1); } if (len == 0) return(-2); dst = src + len; wlen = len / 4; /* Length is bytes */ /* Calibrate the overhead with a zero word copy */ asm __volatile__ ("rdtsc":"=a" (st_low),"=d" (st_high)); for (i=0; iclks_msec)/end_low); } #define rdmsr(msr,val1,val2) \ __asm__ __volatile__("rdmsr" \ : "=a" (val1), "=d" (val2) \ : "c" (msr)) ulong correct_tsc(ulong el_org) { float coef_now, coef_max; int msr_lo, msr_hi, is_xe; rdmsr(0x198, msr_lo, msr_hi); is_xe = (msr_lo >> 31) & 0x1; if(is_xe){ rdmsr(0x198, msr_lo, msr_hi); coef_max = ((msr_hi >> 8) & 0x1F); if ((msr_hi >> 14) & 0x1) { coef_max = coef_max + 0.5f; } } else { rdmsr(0x17, msr_lo, msr_hi); coef_max = ((msr_lo >> 8) & 0x1F); if ((msr_lo >> 14) & 0x1) { coef_max = coef_max + 0.5f; } } if(cpu_id.fid.bits.eist) { rdmsr(0x198, msr_lo, msr_hi); coef_now = ((msr_lo >> 8) & 0x1F); if ((msr_lo >> 14) & 0x1) { coef_now = coef_now + 0.5f; } } else { rdmsr(0x2A, msr_lo, msr_hi); coef_now = (msr_lo >> 22) & 0x1F; } if(coef_max && coef_now) { el_org = (ulong)(el_org * coef_now / coef_max); } return el_org; }