/*
* MemTest86+ V5 Specific code (GPL V2.0)
* By Samuel DEMEULEMEESTER, sdemeule@memtest.org
* http://www.canardpc.com - http://www.memtest.org
* ------------------------------------------------
* main.c - MemTest-86 Version 3.5
*
* Released under version 2 of the Gnu Public License.
* By Chris Brady
*/
#include "stdint.h"
#include "stddef.h"
#include "test.h"
#include "defs.h"
#include "cpuid.h"
#include "smp.h"
#include "config.h"
#undef TEST_TIMES
#define DEFTESTS 9
#define FIRST_DIVISER 3
/* The main stack is allocated during boot time. The stack size should
* preferably be a multiple of page size(4Kbytes)
*/
extern struct cpu_ident cpu_id;
extern char toupper(char c);
extern int isxdigit(char c);
extern void reboot();
extern void bzero();
extern void smp_set_ordinal(int me, int ord);
extern int smp_my_ord_num(int me);
extern int smp_ord_to_cpu(int me);
extern void get_cpuid();
extern void initialise_cpus();
extern ulong rand(int cpu);
extern void get_mem_speed(int cpu, int ncpus);
extern void rand_seed(unsigned int seed1, unsigned int seed2, int cpu);
extern struct barrier_s *barr;
extern int num_cpus;
extern int act_cpus;
static int find_ticks_for_test(int test);
void find_ticks_for_pass(void);
int find_chunks(int test);
static void test_setup(void);
static int compute_segments(struct pmap map, int cpu);
int do_test(int ord);
struct tseq tseq[] =
{
{1, -1, 0, 6, 0, "[Address test, walking ones, no cache] "},
{1, -1, 1, 6, 0, "[Address test, own address Sequential] "},
{1, 32, 2, 6, 0, "[Address test, own address Parallel] "},
{1, 32, 3, 6, 0, "[Moving inversions, 1s & 0s Parallel] "},
{1, 32, 5, 3, 0, "[Moving inversions, 8 bit pattern] "},
{1, 32, 6, 30, 0, "[Moving inversions, random pattern] "},
{1, 32, 7, 81, 0, "[Block move] "},
{1, 1, 8, 3, 0, "[Moving inversions, 32 bit pattern] "},
{1, 32, 9, 48, 0, "[Random number sequence] "},
{1, 32, 10, 6, 0, "[Modulo 20, Random pattern] "},
{1, 1, 11, 240, 0, "[Bit fade test, 2 patterns] "},
{1, 0, 0, 0, 0, NULL}
};
volatile int mstr_cpu;
volatile int run_cpus;
volatile int cpu_ord=0;
int maxcpus=MAX_CPUS;
volatile short cpu_sel;
volatile short cpu_mode;
char cpu_mask[MAX_CPUS];
long bin_mask=0xffffffff;
short onepass;
volatile short btflag = 0;
volatile int test;
short restart_flag;
uint8_t volatile stacks[MAX_CPUS][STACKSIZE_BYTES];
int bitf_seq = 0;
char cmdline_parsed = 0;
struct vars variables = {};
struct vars * const vv = &variables;
volatile int bail;
int nticks;
int test_ticks;
volatile int segs; /* number of entries in vv->map[]
(Why not be a member of vars then?) */
static int ltest;
static int pass_flag = 0;
volatile short start_seq = 0;
static int c_iter;
ulong high_test_adr;
volatile static int window;
volatile static unsigned long win_next;
volatile static ulong win0_start; /* Start test address for window 0 */
volatile static ulong win1_end; /* End address for relocation */
volatile static struct pmap winx; /* Window struct for mapping windows */
/* Find the next selected test to run */
void next_test()
{
test++;
while (tseq[test].sel == 0 && tseq[test].cpu_sel != 0) {
test++;
}
if (tseq[test].cpu_sel == 0) {
/* We hit the end of the list so we completed a pass */
pass_flag++;
/* Find the next test to run, start searching from 0 */
test = 0;
while (tseq[test].sel == 0 && tseq[test].cpu_sel != 0) {
test++;
}
}
}
/* Set default values for all parameters */
void set_defaults()
{
int i;
if (start_seq == 2) {
/* This is a restart so we reset everything */
onepass = 0;
i = 0;
while (tseq[i].cpu_sel) {
tseq[i].sel = 1;
i++;
}
test = 0;
if (tseq[0].sel == 0) {
next_test();
}
}
ltest = -1;
win_next = 0;
window = 0;
bail = 0;
cpu_mode = CPM_ALL;
cpu_sel = 0;
vv->printmode=PRINTMODE_ADDRESSES;
vv->numpatn=0;
vv->plim_lower = 0;
vv->plim_upper = vv->pmap[vv->msegs-1].end;
vv->pass = 0;
vv->msg_line = 0;
vv->ecount = 0;
vv->ecc_ecount = 0;
vv->msg_line = LINE_SCROLL-1;
vv->scroll_start = vv->msg_line * 160;
vv->debugging = 0;
vv->erri.low_addr.page = 0x7fffffff;
vv->erri.low_addr.offset = 0xfff;
vv->erri.high_addr.page = 0;
vv->erri.high_addr.offset = 0;
vv->erri.min_bits = 32;
vv->erri.max_bits = 0;
vv->erri.min_bits = 32;
vv->erri.max_bits = 0;
vv->erri.maxl = 0;
vv->erri.cor_err = 0;
vv->erri.ebits = 0;
vv->erri.hdr_flag = 0;
vv->erri.tbits = 0;
for (i=0; tseq[i].msg != NULL; i++) {
tseq[i].errors = 0;
}
restart_flag = 0;
tseq[10].sel = 0;
}
/* Boot trace function */
short tidx = 25;
void btrace(int me, int line, char *msg, int wait, long v1, long v2)
{
int y, x;
/* Is tracing turned on? */
if (btflag == 0) return;
spin_lock(&barr->mutex);
y = tidx%13;
x = tidx/13*40;
cplace(y+11, x+1, ' ');
if (++tidx > 25) {
tidx = 0;
}
y = tidx%13;
x = tidx/13*40;
cplace(y+11, x+1, '>');
dprint(y+11, x+2, me, 2, 0);
dprint(y+11, x+5, line, 4, 0);
cprint(y+11, x+10, msg);
hprint(y+11, x+22, v1);
hprint(y+11, x+31, v2);
if (wait) {
wait_keyup();
}
spin_unlock(&barr->mutex);
}
/* Relocate the test to a new address. Be careful to not overlap! */
static void run_at(unsigned long addr, int cpu)
{
ulong *ja = (ulong *)(addr + startup_32 - _start);
/* CPU 0, Copy memtest86+ code */
if (cpu == 0) {
mt86_memmove((void *)addr, &_start, _end - _start);
}
/* Wait for the copy */
barrier();
/* We use a lock to insure that only one CPU at a time jumps to
* the new code. Some of the startup stuff is not thread safe! */
spin_lock(&barr->mutex);
/* Jump to the start address */
goto *ja;
}
/* Switch from the boot stack to the main stack. First the main stack
* is allocated, then the contents of the boot stack are copied, then
* ESP is adjusted to point to the new stack.
*/
static void
switch_to_main_stack(unsigned cpu_num)
{
extern uintptr_t boot_stack;
extern uintptr_t boot_stack_top;
uintptr_t *src, *dst;
int offs;
uint8_t * stackAddr, *stackTop;
stackAddr = (uint8_t *) &stacks[cpu_num][0];
stackTop = stackAddr + STACKSIZE_BYTES;
src = (uintptr_t*)&boot_stack_top;
dst = (uintptr_t*)stackTop;
do {
src--; dst--;
*dst = *src;
} while ((uintptr_t *)src > (uintptr_t *)&boot_stack);
offs = (uint8_t *)&boot_stack_top - stackTop;
__asm__ __volatile__ (
"subl %%eax, %%esp"
: /*no output*/
: "a" (offs) : "memory"
);
}
/* command line passing using the 'old' boot protocol */
#define MK_PTR(seg,off) ((void*)(((unsigned long)(seg) << 4) + (off)))
#define OLD_CL_MAGIC_ADDR ((unsigned short*) MK_PTR(INITSEG,0x20))
#define OLD_CL_MAGIC 0xA33F
#define OLD_CL_OFFSET_ADDR ((unsigned short*) MK_PTR(INITSEG,0x22))
static void parse_command_line(void)
{
long simple_strtoul(char *cmd, char *ptr, int base);
char *cp, dummy;
int i, j, k;
if (cmdline_parsed)
return;
/* Fill in the cpu mask array with the default */
for (i=0; i<MAX_CPUS; i++) {
cpu_mask[i] = 1;
}
if (*OLD_CL_MAGIC_ADDR != OLD_CL_MAGIC)
return;
unsigned short offset = *OLD_CL_OFFSET_ADDR;
cp = MK_PTR(INITSEG, offset);
/* skip leading spaces */
while (*cp == ' ')
cp++;
while (*cp) {
if (!mt86_strncmp(cp, "console=", 8)) {
cp += 8;
serial_console_setup(cp);
}
/* Enable boot trace? */
if (!mt86_strncmp(cp, "btrace", 6)) {
cp += 6;
btflag++;
}
/* Limit number of CPUs */
if (!mt86_strncmp(cp, "maxcpus=", 8)) {
cp += 8;
maxcpus=(int)simple_strtoul(cp, &dummy, 10);
}
/* Run one pass and exit if there are no errors */
if (!mt86_strncmp(cp, "onepass", 7)) {
cp += 7;
onepass++;
}
/* Setup a list of tests to run */
if (!mt86_strncmp(cp, "tstlist=", 8)) {
cp += 8;
/* Clear all of the tests first */
k = 0;
while (tseq[k].cpu_sel) {
tseq[k].sel = 0;
k++;
}
/* Now enable all of the tests in the list */
j = 0;
while(*cp && mt86_isdigit(*cp)) {
i = *cp-'0';
j = j*10 + i;
cp++;
if (*cp == ',' || !mt86_isdigit(*cp)) {
if (j < k) {
tseq[j].sel = 1;
}
if (*cp != ',') break;
j = 0;
cp++;
}
}
}
/* Set a CPU mask to select CPU's to use for testing */
if (!mt86_strncmp(cp, "cpumask=", 8)) {
cp += 8;
if (cp[0] == '0' && toupper(cp[1]) == 'X') cp += 2;
while (*cp && *cp != ' ' && isxdigit(*cp)) {
i = mt86_isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10;
bin_mask = bin_mask * 16 + i;
cp++;
}
/* Force CPU zero to always be selected */
bin_mask |= 1;
for (i=0; i<32; i++) {
if (((bin_mask>>i) & 1) == 0) {
cpu_mask[i] = 0;
}
}
}
/* go to the next parameter */
while (*cp && *cp != ' ') cp++;
while (*cp == ' ') cp++;
}
cmdline_parsed = 1;
}
void clear_screen()
{
int i;
char *pp;
/* Clear screen & set background to blue */
for(i=0, pp=(char *)(SCREEN_ADR); i<80*25; i++) {
*pp++ = ' ';
*pp++ = 0x17;
}
if (btflag) {
cprint(1, 0, "Boot Trace Enabled");
cprint(1, 0, "Press any key to advance to next trace point");
cprint(9, 1,"CPU Line Message Param #1 Param #2 CPU Line Message Param #1 Param #2");
cprint(10,1,"--- ---- ----------- -------- -------- --- ---- ----------- -------- --------");
}
}
/* Test entry point. We get here on startup and also whenever
* we relocate. */
void test_start(void)
{
int my_cpu_num, my_cpu_ord, run;
/* If this is the first time here we are CPU 0 */
if (start_seq == 0) {
my_cpu_num = 0;
} else {
my_cpu_num = smp_my_cpu_num();
}
/* First thing, switch to main stack */
switch_to_main_stack(my_cpu_num);
/* First time (for this CPU) initialization */
if (start_seq < 2) {
/* These steps are only done by the boot cpu */
if (my_cpu_num == 0) {
my_cpu_ord = cpu_ord++;
smp_set_ordinal(my_cpu_num, my_cpu_ord);
parse_command_line();
clear_screen();
/* Initialize the barrier so the lock in btrace will work.
* Will get redone later when we know how many CPUs we have */
barrier_init(1);
btrace(my_cpu_num, __LINE__, "Begin ", 1, 0, 0);
/* Find memory size */
mem_size(); /* must be called before initialise_cpus(); */
/* Fill in the CPUID table */
get_cpuid();
/* Startup the other CPUs */
start_seq = 1;
//initialise_cpus();
btrace(my_cpu_num, __LINE__, "BeforeInit", 1, 0, 0);
/* Draw the screen and get system information */
init();
/* Set defaults and initialize variables */
set_defaults();
/* Setup base address for testing, 1 MB */
win0_start = 0x100;
/* Set relocation address to 32Mb if there is enough
* memory. Otherwise set it to 3Mb */
/* Large reloc addr allows for more testing overlap */
if ((ulong)vv->pmap[vv->msegs-1].end > 0x2f00) {
high_test_adr = 0x2000000;
} else {
high_test_adr = 0x300000;
}
win1_end = (high_test_adr >> 12);
/* Adjust the map to not test the page at 939k,
* reserved for locks */
vv->pmap[0].end--;
find_ticks_for_pass();
} else {
/* APs only, Register the APs */
btrace(my_cpu_num, __LINE__, "AP_Start ", 0, my_cpu_num,
cpu_ord);
smp_ap_booted(my_cpu_num);
/* Asign a sequential CPU ordinal to each active cpu */
spin_lock(&barr->mutex);
my_cpu_ord = cpu_ord++;
smp_set_ordinal(my_cpu_num, my_cpu_ord);
spin_unlock(&barr->mutex);
btrace(my_cpu_num, __LINE__, "AP_Done ", 0, my_cpu_num,
my_cpu_ord);
}
} else {
/* Unlock after a relocation */
spin_unlock(&barr->mutex);
/* Get the CPU ordinal since it is lost during relocation */
my_cpu_ord = smp_my_ord_num(my_cpu_num);
btrace(my_cpu_num, __LINE__, "Reloc_Done",0,my_cpu_num,my_cpu_ord);
}
/* A barrier to insure that all of the CPUs are done with startup */
barrier();
btrace(my_cpu_num, __LINE__, "1st Barr ", 1, my_cpu_num, my_cpu_ord);
/* Setup Memory Management and measure memory speed, we do it here
* because we need all of the available CPUs */
if (start_seq < 2) {
/* Enable floating point processing */
if (cpu_id.fid.bits.fpu)
__asm__ __volatile__
(
"movl %%cr0, %%eax\n\t"
"andl $0x7, %%eax\n\t"
"movl %%eax, %%cr0\n\t"
: :
: "ax"
);
if (cpu_id.fid.bits.sse)
__asm__ __volatile__
(
"movl %%cr4, %%eax\n\t"
"orl $0x00000200, %%eax\n\t"
"movl %%eax, %%cr4\n\t"
: :
: "ax"
);
btrace(my_cpu_num, __LINE__, "Mem Mgmnt ",
1, cpu_id.fid.bits.pae, cpu_id.fid.bits.lm);
/* Setup memory management modes */
/* If we have PAE, turn it on */
if (cpu_id.fid.bits.pae == 1) {
__asm__ __volatile__
(
"movl %%cr4, %%eax\n\t"
"orl $0x00000020, %%eax\n\t"
"movl %%eax, %%cr4\n\t"
: :
: "ax"
);
cprint(LINE_TITLE+1, COL_MODE, "(PAE Mode)");
}
/* If this is a 64 CPU enable long mode */
if (cpu_id.fid.bits.lm == 1) {
__asm__ __volatile__
(
"movl $0xc0000080, %%ecx\n\t"
"rdmsr\n\t"
"orl $0x00000100, %%eax\n\t"
"wrmsr\n\t"
: :
: "ax", "cx"
);
cprint(LINE_TITLE+1, COL_MODE, "(X64 Mode)");
}
/* Get the memory Speed with all CPUs */
get_mem_speed(my_cpu_num, num_cpus);
}
/* Set the initialized flag only after all of the CPU's have
* Reached the barrier. This insures that relocation has
* been completed for each CPU. */
btrace(my_cpu_num, __LINE__, "Start Done", 1, 0, 0);
start_seq = 2;
/* Loop through all tests */
while (1) {
/* If the restart flag is set all initial params */
if (restart_flag) {
set_defaults();
continue;
}
/* Skip single CPU tests if we are using only one CPU */
if (tseq[test].cpu_sel == -1 &&
(num_cpus == 1 || cpu_mode != CPM_ALL)) {
test++;
continue;
}
test_setup();
/* Loop through all possible windows */
while (win_next <= ((ulong)vv->pmap[vv->msegs-1].end + WIN_SZ_PAGES)) {
/* Main scheduling barrier */
cprint(8, my_cpu_num+7, "W");
btrace(my_cpu_num, __LINE__, "Sched_Barr", 1,window,win_next);
barrier();
/* Don't go over the 8TB PAE limit */
if (win_next > MAX_MEM_PAGES) {
break;
}
/* For the bit fade test, #11, we cannot relocate so bump the
* window to 1 */
if (tseq[test].pat == 11 && window == 0) {
window = 1;
}
/* Relocate if required */
if (window != 0 && (ulong)&_start != LOW_TEST_ADR) {
btrace(my_cpu_num, __LINE__, "Sched_RelL", 1,0,0);
run_at(LOW_TEST_ADR, my_cpu_num);
}
if (window == 0 && vv->plim_lower >= win0_start) {
window++;
}
if (window == 0 && (ulong)&_start == LOW_TEST_ADR) {
btrace(my_cpu_num, __LINE__, "Sched_RelH", 1,0,0);
run_at(high_test_adr, my_cpu_num);
}
/* Decide which CPU(s) to use */
btrace(my_cpu_num, __LINE__, "Sched_CPU0",1,cpu_sel,
tseq[test].cpu_sel);
run = 1;
switch(cpu_mode) {
case CPM_RROBIN:
case CPM_SEQ:
/* Select a single CPU */
if (my_cpu_ord == cpu_sel) {
mstr_cpu = cpu_sel;
run_cpus = 1;
} else {
run = 0;
}
break;
case CPM_ALL:
/* Use all CPUs */
if (tseq[test].cpu_sel == -1) {
/* Round robin through all of the CPUs */
if (my_cpu_ord == cpu_sel) {
mstr_cpu = cpu_sel;
run_cpus = 1;
} else {
run = 0;
}
} else {
/* Use the number of CPUs specified by the test,
* Starting with zero */
if (my_cpu_ord >= tseq[test].cpu_sel) {
run = 0;
}
/* Set the master CPU to the highest CPU number
* that has been selected */
if (act_cpus < tseq[test].cpu_sel) {
mstr_cpu = act_cpus-1;
run_cpus = act_cpus;
} else {
mstr_cpu = tseq[test].cpu_sel-1;
run_cpus = tseq[test].cpu_sel;
}
}
}
btrace(my_cpu_num, __LINE__, "Sched_CPU1",1,run_cpus,run);
barrier();
dprint(9, 7, run_cpus, 2, 0);
/* Setup a sub barrier for only the selected CPUs */
if (my_cpu_ord == mstr_cpu) {
s_barrier_init(run_cpus);
}
/* Make sure the the sub barrier is ready before proceeding */
barrier();
/* Not selected CPUs go back to the scheduling barrier */
if (run == 0 ) {
continue;
}
cprint(8, my_cpu_num+7, "-");
btrace(my_cpu_num, __LINE__, "Sched_Win0",1,window,win_next);
if (my_cpu_ord == mstr_cpu) {
switch (window) {
/* Special case for relocation */
case 0:
winx.start = 0;
winx.end = win1_end;
window++;
break;
/* Special case for first segment */
case 1:
winx.start = win0_start;
winx.end = WIN_SZ_PAGES;
win_next += WIN_SZ_PAGES;
window++;
break;
/* For all other windows */
default:
winx.start = win_next;
win_next += WIN_SZ_PAGES;
winx.end = win_next;
}
btrace(my_cpu_num,__LINE__,"Sched_Win1",1,winx.start,
winx.end);
/* Find the memory areas to test */
segs = compute_segments(winx, my_cpu_num);
}
s_barrier();
btrace(my_cpu_num,__LINE__,"Sched_Win2",1,segs,
vv->map[0].pbase_addr);
if (segs == 0) {
/* No memory in this window so skip it */
continue;
}
/* map in the window... */
if (map_page(vv->map[0].pbase_addr) < 0) {
/* Either there is no PAE or we are at the PAE limit */
break;
}
btrace(my_cpu_num, __LINE__, "Strt_Test ",1,my_cpu_num,
my_cpu_ord);
do_test(my_cpu_ord);
btrace(my_cpu_num, __LINE__, "End_Test ",1,my_cpu_num,
my_cpu_ord);
paging_off();
} /* End of window loop */
s_barrier();
btrace(my_cpu_num, __LINE__, "End_Win ",1,test, window);
/* Setup for the next set of windows */
win_next = 0;
window = 0;
bail = 0;
/* Only the master CPU does the end of test housekeeping */
if (my_cpu_ord != mstr_cpu) {
continue;
}
/* Special handling for the bit fade test #11 */
if (tseq[test].pat == 11 && bitf_seq != 6) {
/* Keep going until the sequence is complete. */
bitf_seq++;
continue;
} else {
bitf_seq = 0;
}
/* Select advancement of CPUs and next test */
switch(cpu_mode) {
case CPM_RROBIN:
if (++cpu_sel >= act_cpus) {
cpu_sel = 0;
}
next_test();
break;
case CPM_SEQ:
if (++cpu_sel >= act_cpus) {
cpu_sel = 0;
next_test();
}
break;
case CPM_ALL:
if (tseq[test].cpu_sel == -1)
{
/* Do the same test for each CPU */
if (++cpu_sel >= act_cpus)
{
cpu_sel = 0;
next_test();
} else {
continue;
}
} else {
next_test();
}
} //????
btrace(my_cpu_num, __LINE__, "Next_CPU ",1,cpu_sel,test);
/* If this was the last test then we finished a pass */
if (pass_flag)
{
pass_flag = 0;
vv->pass++;
dprint(LINE_INFO, 49, vv->pass, 5, 0);
find_ticks_for_pass();
ltest = -1;
if (vv->ecount == 0)
{
/* If onepass is enabled and we did not get any errors
* reboot to exit the test */
if (onepass) { reboot(); }
if (!btflag)
cprint(LINE_MSG, COL_MSG-8,
"** Pass complete, no errors, press Esc to exit **");
if(BEEP_END_NO_ERROR)
{
beep(1000);
beep(2000);
beep(1000);
beep(2000);
}
}
}
bail=0;
} /* End test loop */
}
void test_setup()
{
static int ltest = -1;
/* See if a specific test has been selected */
if (vv->testsel >= 0) {
test = vv->testsel;
}
/* Only do the setup if this is a new test */
if (test == ltest) {
return;
}
ltest = test;
/* Now setup the test parameters based on the current test number */
if (vv->pass == 0) {
/* Reduce iterations for first pass */
c_iter = tseq[test].iter/FIRST_DIVISER;
} else {
c_iter = tseq[test].iter;
}
/* Set the number of iterations. We only do half of the iterations */
/* on the first pass */
//dprint(LINE_INFO, 28, c_iter, 3, 0);
test_ticks = find_ticks_for_test(test);
nticks = 0;
vv->tptr = 0;
cprint(LINE_PAT, COL_PAT, " ");
cprint(LINE_PAT, COL_PAT-3, " ");
dprint(LINE_TST, COL_MID+6, tseq[test].pat, 2, 1);
cprint(LINE_TST, COL_MID+9, tseq[test].msg);
cprint(2, COL_MID+8, " ");
}
/* A couple static variables for when all cpus share the same pattern */
static ulong sp1, sp2;
int do_test(int my_ord)
{
int i=0, j=0;
static int bitf_sleep;
unsigned long p0=0, p1=0, p2=0;
if (my_ord == mstr_cpu) {
if ((ulong)&_start > LOW_TEST_ADR) {
/* Relocated so we need to test all selected lower memory */
vv->map[0].start = mapping(vv->plim_lower);
/* Good 'ol Legacy USB_WAR */
if (vv->map[0].start < (ulong*)0x500)
{
vv->map[0].start = (ulong*)0x500;
}
cprint(LINE_PAT, COL_MID+25, " R");
} else {
cprint(LINE_PAT, COL_MID+25, " ");
}
/* Update display of memory segments being tested */
p0 = page_of(vv->map[0].start);
p1 = page_of(vv->map[segs-1].end);
aprint(LINE_RANGE, COL_MID+9, p0);
cprint(LINE_RANGE, COL_MID+14, " - ");
aprint(LINE_RANGE, COL_MID+17, p1);
aprint(LINE_RANGE, COL_MID+25, p1-p0);
cprint(LINE_RANGE, COL_MID+30, " of ");
aprint(LINE_RANGE, COL_MID+34, vv->selected_pages);
}
switch(tseq[test].pat) {
/* do the testing according to the selected pattern */
case 0: /* Address test, walking ones (test #0) */
/* Run with cache turned off */
set_cache(0);
addr_tst1(my_ord);
set_cache(1);
BAILOUT;
break;
case 1:
case 2: /* Address test, own address (test #1, 2) */
addr_tst2(my_ord);
BAILOUT;
break;
case 3:
case 4: /* Moving inversions, all ones and zeros (tests #3, 4) */
p1 = 0;
p2 = ~p1;
s_barrier();
movinv1(c_iter,p1,p2,my_ord);
BAILOUT;
/* Switch patterns */
s_barrier();
movinv1(c_iter,p2,p1,my_ord);
BAILOUT;
break;
case 5: /* Moving inversions, 8 bit walking ones and zeros (test #5) */
p0 = 0x80;
for (i=0; i<8; i++, p0=p0>>1) {
p1 = p0 | (p0<<8) | (p0<<16) | (p0<<24);
p2 = ~p1;
s_barrier();
movinv1(c_iter,p1,p2, my_ord);
BAILOUT;
/* Switch patterns */
s_barrier();
movinv1(c_iter,p2,p1, my_ord);
BAILOUT;
}
break;
case 6: /* Random Data (test #6) */
/* Seed the random number generator */
if (my_ord == mstr_cpu) {
if (cpu_id.fid.bits.rdtsc) {
asm __volatile__ ("rdtsc":"=a" (sp1),"=d" (sp2));
} else {
sp1 = 521288629 + vv->pass;
sp2 = 362436069 - vv->pass;
}
rand_seed(sp1, sp2, 0);
}
s_barrier();
for (i=0; i < c_iter; i++) {
if (my_ord == mstr_cpu) {
sp1 = rand(0);
sp2 = ~p1;
}
s_barrier();
movinv1(2,sp1,sp2, my_ord);
BAILOUT;
}
break;
case 7: /* Block move (test #7) */
block_move(c_iter, my_ord);
BAILOUT;
break;
case 8: /* Moving inversions, 32 bit shifting pattern (test #8) */
for (i=0, p1=1; p1; p1=p1<<1, i++) {
s_barrier();
movinv32(c_iter,p1, 1, 0x80000000, 0, i, my_ord);
{ BAILOUT }
s_barrier();
movinv32(c_iter,~p1, 0xfffffffe,
0x7fffffff, 1, i, my_ord);
{ BAILOUT }
}
break;
case 9: /* Random Data Sequence (test #9) */
for (i=0; i < c_iter; i++) {
s_barrier();
movinvr(my_ord);
BAILOUT;
}
break;
case 10: /* Modulo 20 check, Random pattern (test #10) */
for (j=0; j<c_iter; j++) {
p1 = rand(0);
for (i=0; i<MOD_SZ; i++) {
p2 = ~p1;
s_barrier();
modtst(i, 2, p1, p2, my_ord);
BAILOUT;
/* Switch patterns */
s_barrier();
modtst(i, 2, p2, p1, my_ord);
BAILOUT;
}
}
break;
case 11: /* Bit fade test, fill (test #11) */
/* Use a sequence to process all windows for each stage */
switch(bitf_seq) {
case 0: /* Fill all of memory 0's */
bit_fade_fill(0, my_ord);
bitf_sleep = 1;
break;
case 1: /* Sleep for the specified time */
/* Only sleep once */
if (bitf_sleep) {
sleep(c_iter, 1, my_ord, 0);
bitf_sleep = 0;
}
break;
case 2: /* Now check all of memory for changes */
bit_fade_chk(0, my_ord);
break;
case 3: /* Fill all of memory 1's */
bit_fade_fill(-1, my_ord);
bitf_sleep = 1;
break;
case 4: /* Sleep for the specified time */
/* Only sleep once */
if (bitf_sleep) {
sleep(c_iter, 1, my_ord, 0);
bitf_sleep = 0;
}
break;
case 5: /* Now check all of memory for changes */
bit_fade_chk(-1, my_ord);
break;
}
BAILOUT;
break;
case 90: /* Modulo 20 check, all ones and zeros (unused) */
p1=0;
for (i=0; i<MOD_SZ; i++) {
p2 = ~p1;
modtst(i, c_iter, p1, p2, my_ord);
BAILOUT;
/* Switch patterns */
p2 = p1;
p1 = ~p2;
modtst(i, c_iter, p1,p2, my_ord);
BAILOUT;
}
break;
case 91: /* Modulo 20 check, 8 bit pattern (unused) */
p0 = 0x80;
for (j=0; j<8; j++, p0=p0>>1) {
p1 = p0 | (p0<<8) | (p0<<16) | (p0<<24);
for (i=0; i<MOD_SZ; i++) {
p2 = ~p1;
modtst(i, c_iter, p1, p2, my_ord);
BAILOUT;
/* Switch patterns */
p2 = p1;
p1 = ~p2;
modtst(i, c_iter, p1, p2, my_ord);
BAILOUT;
}
}
break;
}
return(0);
}
/* Compute number of SPINSZ_DWORDS chunks being tested */
int find_chunks(int tst)
{
int i, j, sg, wmax, ch;
struct pmap twin={0,0};
unsigned long wnxt = WIN_SZ_PAGES;
unsigned long len;
wmax = MAX_MEM_PAGES/WIN_SZ_PAGES+2; /* The number of 2 GB segments +2 */
/* Compute the number of SPINSZ_DWORDS memory segments */
ch = 0;
for(j = 0; j < wmax; j++) {
/* special case for relocation */
if (j == 0) {
twin.start = 0;
twin.end = win1_end;
}
/* special case for first 2 GB */
if (j == 1) {
twin.start = win0_start;
twin.end = WIN_SZ_PAGES;
}
/* For all other windows */
if (j > 1) {
twin.start = wnxt;
wnxt += WIN_SZ_PAGES;
twin.end = wnxt;
}
/* Find the memory areas I am going to test */
sg = compute_segments(twin, -1);
for(i = 0; i < sg; i++) {
len = vv->map[i].end - vv->map[i].start;
if (cpu_mode == CPM_ALL && num_cpus > 1) {
switch(tseq[tst].pat) {
case 2:
case 4:
case 5:
case 6:
case 9:
case 10:
len /= act_cpus;
break;
case 7:
case 8:
len /= act_cpus;
break;
}
}
ch += (len + SPINSZ_DWORDS -1)/SPINSZ_DWORDS;
}
}
return(ch);
}
/* Compute the total number of ticks per pass */
void find_ticks_for_pass(void)
{
int i;
vv->pptr = 0;
vv->pass_ticks = 0;
vv->total_ticks = 0;
cprint(1, COL_MID+8, " ");
i = 0;
while (tseq[i].cpu_sel != 0) {
/* Skip tests 2 and 4 if we are using 1 cpu */
if (act_cpus == 1 && (i == 2 || i == 4)) {
i++;
continue;
}
vv->pass_ticks += find_ticks_for_test(i);
i++;
}
}
static int find_ticks_for_test(int tst)
{
int ticks=0, iter, ch;
if (tseq[tst].sel == 0) {
return(0);
}
/* Determine the number of SPINSZ chunks for this test */
ch = find_chunks(tst);
/* Set the number of iterations. We only do 1/2 of the iterations */
/* on the first pass */
if (vv->pass == 0) {
iter = tseq[tst].iter/FIRST_DIVISER;
} else {
iter = tseq[tst].iter;
}
switch(tseq[tst].pat) {
case 0: /* Address test, walking ones */
ticks = ch;
break;
case 1: /* Address test, own address */
case 2:
ticks = ch * 2;
break;
case 3: /* Moving inversions, all ones and zeros */
case 4: {
const int each_movinv1 = ch * (1 + 2 * iter); // each movinv1()
ticks = 2 * each_movinv1; // which we call twice
break;
}
case 5: { /* Moving inversions, 8 bit walking ones and zeros */
const int each_movinv1 = ch * (1 + 2 * iter); // same as above
ticks = 16 * each_movinv1; // but here we call it 16x
break;
}
case 6: { /* Random Data */
const int each_movinv1 = ch * 5; // movinv1() does only 5 ticks here
ticks = iter * each_movinv1; // and we call it 'iter' times.
break;
}
case 7: /* Block move */
ticks = ch * (2 + iter);
break;
case 8: { /* Moving inversions, 32 bit shifting pattern */
const int each_movinv32 = ch * (1 + 2 * iter); // Each movinv32()
ticks = each_movinv32 * 64; // We call it 64x
break;
}
case 9: { /* Random Data Sequence */
const int each_movinvr = 3 * ch; // Each movinvr() ticks 3*ch times
ticks = each_movinvr * iter; // We call it iter times
break;
}
case 10: { /* Modulo 20 check, Random pattern */
const int each_modtst = ch * 4;
ticks = iter * MOD_SZ * each_modtst;
break;
}
case 11: { /* Bit fade test */
// Each call to bit_fade_fill() and bit_fade_chk() ticks ch times.
const int each_bit_fade_fill = ch;
const int each_bit_fade_chk = ch;
// We call each twice: fill 0s, check, fill 1s, check.
const int loop_ticks = 2 * each_bit_fade_chk + 2 * each_bit_fade_fill;
// We also sleep for '2*iter' seconds and tick once per second
const int sleep_ticks = iter * 2;
ticks = loop_ticks + sleep_ticks;
break;
}
case 90: { /* Modulo 20 check, all ones and zeros (unused) */
const int each_modtst = ch * (2 + iter);
ticks = each_modtst * 2 * MOD_SZ;
break;
}
case 91: { /* Modulo 20 check, 8 bit pattern (unused) */
const int each_modtst = ch * (2 + iter);
ticks = each_modtst * 2 * MOD_SZ * 8;
break;
}
default:
ASSERT(0);
break;
}
if (cpu_mode == CPM_SEQ || tseq[tst].cpu_sel == -1) {
ticks *= act_cpus;
}
return ticks;
}
static int compute_segments(struct pmap win, int me)
{
unsigned long wstart, wend;
int i, sg;
/* Compute the window I am testing memory in */
wstart = win.start;
wend = win.end;
sg = 0;
/* Now reduce my window to the area of memory I want to test */
if (wstart < vv->plim_lower) {
wstart = vv->plim_lower;
}
if (wend > vv->plim_upper) {
wend = vv->plim_upper;
}
if (wstart >= wend) {
return(0);
}
/* List the segments being tested */
for (i=0; i< vv->msegs; i++) {
unsigned long start, end;
start = vv->pmap[i].start;
end = vv->pmap[i].end;
if (start <= wstart) {
start = wstart;
}
if (end >= wend) {
end = wend;
}
#if 0
cprint(LINE_SCROLL+(2*i), 0, " (");
hprint(LINE_SCROLL+(2*i), 2, start);
cprint(LINE_SCROLL+(2*i), 10, ", ");
hprint(LINE_SCROLL+(2*i), 12, end);
cprint(LINE_SCROLL+(2*i), 20, ") ");
cprint(LINE_SCROLL+(2*i), 22, "r(");
hprint(LINE_SCROLL+(2*i), 24, wstart);
cprint(LINE_SCROLL+(2*i), 32, ", ");
hprint(LINE_SCROLL+(2*i), 34, wend);
cprint(LINE_SCROLL+(2*i), 42, ") ");
cprint(LINE_SCROLL+(2*i), 44, "p(");
hprint(LINE_SCROLL+(2*i), 46, vv->plim_lower);
cprint(LINE_SCROLL+(2*i), 54, ", ");
hprint(LINE_SCROLL+(2*i), 56, vv->plim_upper);
cprint(LINE_SCROLL+(2*i), 64, ") ");
cprint(LINE_SCROLL+(2*i+1), 0, "w(");
hprint(LINE_SCROLL+(2*i+1), 2, win.start);
cprint(LINE_SCROLL+(2*i+1), 10, ", ");
hprint(LINE_SCROLL+(2*i+1), 12, win.end);
cprint(LINE_SCROLL+(2*i+1), 20, ") ");
cprint(LINE_SCROLL+(2*i+1), 22, "m(");
hprint(LINE_SCROLL+(2*i+1), 24, vv->pmap[i].start);
cprint(LINE_SCROLL+(2*i+1), 32, ", ");
hprint(LINE_SCROLL+(2*i+1), 34, vv->pmap[i].end);
cprint(LINE_SCROLL+(2*i+1), 42, ") ");
cprint(LINE_SCROLL+(2*i+1), 44, "i=");
hprint(LINE_SCROLL+(2*i+1), 46, i);
cprint(LINE_SCROLL+(2*i+2), 0,
" "
" ");
cprint(LINE_SCROLL+(2*i+3), 0,
" "
" ");
#endif
if ((start < end) && (start < wend) && (end > wstart)) {
vv->map[sg].pbase_addr = start;
vv->map[sg].start = mapping(start);
vv->map[sg].end = emapping(end);
#if 0
hprint(LINE_SCROLL+(sg+1), 0, sg);
hprint(LINE_SCROLL+(sg+1), 12, vv->map[sg].pbase_addr);
hprint(LINE_SCROLL+(sg+1), 22, start);
hprint(LINE_SCROLL+(sg+1), 32, end);
hprint(LINE_SCROLL+(sg+1), 42, mapping(start));
hprint(LINE_SCROLL+(sg+1), 52, emapping(end));
cprint(LINE_SCROLL+(sg+2), 0,
" "
" ");
#endif
#if 0
cprint(LINE_SCROLL+(2*i+1), 54, ", sg=");
hprint(LINE_SCROLL+(2*i+1), 59, sg);
#endif
sg++;
}
}
return (sg);
}