| /* 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 "smp.h" |
| #undef TEST_TIMES |
| #define DEFTESTS 12 |
| |
| /* The main stack is allocated during boot time. The stack size should |
| * preferably be a multiple of page size(4Kbytes) |
| */ |
| #define STACKSIZE (6*1024) |
| #define MAX_MEM 0x1000000 /* 64 GB */ |
| #define TWO_GB 0x80000 /* 2 GB */ |
| |
| extern void bzero(); |
| 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); |
| |
| const struct tseq tseq[] = { |
| {-1, 0, 6, 0, "[Address test, walking ones, no cache] "}, |
| {-1, 1, 6, 0, "[Address test, own address Seqential] "}, |
| {16, 2, 6, 0, "[Address test, own address Parallel] "}, |
| {-1, 3, 6, 0, "[Moving inversions, 1s & 0s] Seqential]"}, |
| {16, 4, 6, 0, "[Moving inversions, 1s & 0s] Parallel] "}, |
| {16, 5, 3, 0, "[Moving inversions, 8 bit pattern] "}, |
| {16, 6, 30, 0, "[Moving inversions, random pattern] "}, |
| {16, 7, 81, 0, "[Block move] "}, |
| {16, 8, 3, 0, "[Moving inversions, 32 bit pattern] "}, |
| {16, 9, 24, 0, "[Random number sequence] "}, |
| {16, 10, 6, 0, "[Modulo 20, Random pattern] "}, |
| {1, 11, 300, 0, "[Bit fade test, 2 patterns] "}, |
| {0, 0, 0, 0, NULL} |
| }; |
| |
| extern struct barrier_s *barr; |
| extern unsigned num_cpus; |
| volatile int mstr_cpu; |
| volatile int run_cpus; |
| volatile int test; |
| volatile short cpu_sel = 0; |
| bool smp_mode = TRUE; |
| bool restart_pending = FALSE; |
| uint8_t volatile stacks[MAX_CPUS][STACKSIZE]; |
| int bitf_seq = 0; |
| |
| char cmdline_parsed = 0; |
| struct vars variables = {}; |
| struct vars * const v = &variables; |
| |
| volatile int bail = 0; |
| int test_ticks; |
| volatile int segs; |
| int nticks; |
| ulong high_test_adr; |
| |
| volatile short start_seq = 0; |
| volatile short cpu_mode = CPM_ALL; |
| static int c_iter; |
| 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 */ |
| |
| #if (LOW_TEST_ADR > (400*1024)) |
| #error LOW_TEST_ADR must be below 400K |
| #endif |
| |
| 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 do_test(int cpu); |
| |
| /* 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) { |
| 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; |
| |
| 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" |
| ); |
| } |
| |
| void restart_internal(int cpu) |
| { |
| /* clear variables */ |
| smp_mode = TRUE; |
| restart_pending = FALSE; |
| |
| run_at(LOW_TEST_ADR, cpu); |
| } |
| |
| void restart(void) |
| { |
| bail++; |
| restart_pending = TRUE; |
| } |
| |
| void initialise_cpus(void) |
| { |
| int cpu_num; |
| |
| smp_init_bsp(); |
| |
| /* Initialize the barrier before starting AP's */ |
| barrier_init(num_cpus); |
| |
| /* let the BSP initialise the APs. */ |
| for(cpu_num = 1; cpu_num < num_cpus; cpu_num++) { |
| smp_boot_ap(cpu_num); |
| } |
| } |
| |
| /* 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) |
| { |
| char *cmdline; |
| |
| if (cmdline_parsed) |
| return; |
| |
| if (*OLD_CL_MAGIC_ADDR != OLD_CL_MAGIC) |
| return; |
| |
| unsigned short offset = *OLD_CL_OFFSET_ADDR; |
| cmdline = MK_PTR(INITSEG, offset); |
| |
| /* skip leading spaces */ |
| while (*cmdline == ' ') |
| cmdline++; |
| |
| while (*cmdline) { |
| if (!strncmp(cmdline, "console=", 8)) { |
| cmdline += 8; |
| serial_console_setup(cmdline); |
| } |
| |
| /* go to the next parameter */ |
| while (*cmdline && *cmdline != ' ') |
| cmdline++; |
| while (*cmdline == ' ') |
| cmdline++; |
| } |
| |
| cmdline_parsed = 1; |
| } |
| |
| /* This is the test entry point. We get here on statup and also whenever |
| * we relocate. */ |
| void test_start(void) |
| { |
| int my_cpu_num, run; |
| |
| |
| /* First thing, switch to main stack */ |
| my_cpu_num = smp_my_cpu_num(); |
| switch_to_main_stack(my_cpu_num); |
| |
| /* First time initialization */ |
| if (start_seq == 0) { |
| |
| /* These steps are only done by the boot cpu */ |
| if (my_cpu_num == 0) { |
| parse_command_line(); |
| mem_size(); /* must be called before initialise_cpus(); */ |
| initialise_cpus(); |
| init(); |
| |
| test = 0; |
| win_next = 0; |
| window = 0; |
| bail = 0; |
| |
| /* Setup base address for testing */ |
| win0_start = (LOW_TEST_ADR+(_end - _start)+8191) >> 12; |
| |
| /* 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)v->pmap[v->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 */ |
| v->pmap[0].end--; |
| |
| find_ticks_for_pass(); |
| } else { |
| /* AP only, Register the APs */ |
| smp_ap_booted(my_cpu_num); |
| } |
| } else { |
| /* Unlock after a relocation restart */ |
| spin_unlock(&barr->mutex); |
| } |
| |
| /* A barrier to insure that all of the CPUs are done with startup */ |
| barrier(); |
| |
| /* Measure memory speed, we do it here because we need all of the |
| * available CPUs */ |
| if (start_seq == 0) { |
| 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. */ |
| start_seq = 1; |
| |
| /* Loop through all tests */ |
| while (1) { |
| /* Skip tests 2 and 4 if we are using only one CPU */ |
| if (tseq[test].pat == 2 || tseq[test].pat == 4) { |
| if (num_cpus == 1 || cpu_mode != CPM_ALL) { |
| test++; |
| continue; |
| } |
| } |
| |
| test_setup(); |
| |
| /* Loop through all possible windows */ |
| while (win_next <= ((ulong)v->pmap[v->msegs-1].end + TWO_GB)) { |
| |
| /* Main scheduling barrier */ |
| cprint(8, 2*my_cpu_num+7, "W"); |
| barrier(); |
| |
| /* Don't go over the 64 GB PAE limit */ |
| if (win_next > MAX_MEM) { |
| 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) { |
| run_at(LOW_TEST_ADR, my_cpu_num); |
| } |
| if (window == 0 && (ulong)&_start == LOW_TEST_ADR) { |
| run_at(high_test_adr, my_cpu_num); |
| } |
| |
| /* Decide which CPU(s) to use */ |
| run = 1; |
| switch(cpu_mode) { |
| case CPM_RROBIN: |
| case CPM_SEQ: |
| /* Select a single CPU */ |
| if (my_cpu_num == 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_num == 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_num >= tseq[test].cpu_sel) { |
| run = 0; |
| } |
| /* Set the master CPU to the highest CPU number |
| * that has been selected */ |
| if (num_cpus < tseq[test].cpu_sel) { |
| mstr_cpu = num_cpus-1; |
| run_cpus = num_cpus; |
| } else { |
| mstr_cpu = tseq[test].cpu_sel-1; |
| run_cpus = tseq[test].cpu_sel; |
| } |
| } |
| } |
| barrier(); |
| dprint(8, 54, run_cpus, 2, 0); |
| |
| /* Setup a sub barrier for only the selected CPUs */ |
| if (my_cpu_num == 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, 2*my_cpu_num+7, "-"); |
| |
| /* Do we need to exit */ |
| if(restart_pending) { |
| restart_internal(my_cpu_num); |
| } |
| |
| if (my_cpu_num == mstr_cpu) { |
| switch (window) { |
| /* Special case for relocation */ |
| case 0: |
| winx.start = 0; |
| winx.end = win1_end; |
| window++; |
| break; |
| /* Special case for first 2 GB */ |
| case 1: |
| winx.start = win0_start; |
| winx.end = TWO_GB; |
| win_next += TWO_GB; |
| window++; |
| break; |
| /* For all other windows */ |
| default: |
| winx.start = win_next; |
| win_next += TWO_GB; |
| winx.end = win_next; |
| } |
| |
| /* Find the memory areas to test */ |
| segs = compute_segments(winx); |
| } |
| s_barrier(); |
| |
| if (segs == 0) { |
| /* No memory in this window so skip it */ |
| continue; |
| } |
| |
| /* map in the window... */ |
| if (map_page(v->map[0].pbase_addr) < 0) { |
| continue; |
| } |
| |
| do_test(my_cpu_num); |
| if (bail) { |
| break; |
| } |
| |
| paging_off(); |
| |
| } /* End of window loop */ |
| |
| s_barrier(); |
| |
| /* 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_num != 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 >= num_cpus) { |
| cpu_sel = 0; |
| } |
| test++; |
| break; |
| case CPM_SEQ: |
| if (++cpu_sel >= num_cpus) { |
| cpu_sel = 0; |
| test++; |
| } |
| break; |
| case CPM_ALL: |
| if (tseq[test].cpu_sel == -1) { |
| /* Do the same test for each CPU */ |
| if (++cpu_sel >= num_cpus) { |
| cpu_sel = 0; |
| test++; |
| } else { |
| continue; |
| } |
| } else { |
| test++; |
| } |
| } |
| |
| /* If this was the last test then we finished a pass */ |
| if (test >= DEFTESTS || |
| (v->testsel >= 0 && cpu_sel == (num_cpus-1))) { |
| v->pass++; |
| dprint(LINE_INFO, 55, v->pass, 5, 0); |
| v->total_ticks = 0; |
| find_ticks_for_pass(); |
| cprint(1, COL_MID+8, |
| " "); |
| if (v->ecount == 0 && v->testsel < 0) { |
| cprint(LINE_MSG, COL_MSG, |
| "Pass complete, no errors, press Esc to exit"); |
| } |
| } |
| if (test >= DEFTESTS) { |
| test = 0; |
| } |
| |
| bail=0; |
| } /* End test loop */ |
| } |
| |
| void test_setup() |
| { |
| static int ltest = -1; |
| |
| /* 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 (v->pass == 0) { |
| /* Reduce iterations for first pass */ |
| c_iter = tseq[test].iter/3; |
| } 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; |
| v->tptr = 0; |
| |
| cprint(LINE_PAT, COL_PAT, " "); |
| cprint(LINE_PAT, COL_PAT-3, " "); |
| dprint(LINE_TST, COL_MID+6, test, 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_cpu) |
| { |
| int i=0, j=0; |
| static int bitf_sleep; |
| unsigned long p0=0, p1=0, p2=0; |
| |
| if (my_cpu == mstr_cpu) { |
| if ((ulong)&_start > LOW_TEST_ADR) { |
| /* Relocated so we need to test all selected lower memory */ |
| v->map[0].start = mapping(v->plim_lower); |
| cprint(LINE_PAT, COL_MID+28, " Relocated"); |
| } else { |
| cprint(LINE_PAT, COL_MID+28, " "); |
| } |
| |
| /* Update display of memory segments being tested */ |
| p0 = page_of(v->map[0].start); |
| p1 = page_of(v->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, v->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_cpu); |
| set_cache(1); |
| BAILOUT; |
| break; |
| |
| case 1: |
| case 2: /* Address test, own address (test #1, 2) */ |
| addr_tst2(my_cpu); |
| 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_cpu); |
| BAILOUT; |
| |
| /* Switch patterns */ |
| s_barrier(); |
| movinv1(c_iter,p2,p1,my_cpu); |
| 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_cpu); |
| BAILOUT; |
| |
| /* Switch patterns */ |
| s_barrier(); |
| movinv1(c_iter,p2,p1, my_cpu); |
| BAILOUT |
| } |
| break; |
| |
| case 6: /* Random Data (test #6) */ |
| /* Seed the random number generator */ |
| if (my_cpu == mstr_cpu) { |
| if (v->rdtsc) { |
| asm __volatile__ ("rdtsc":"=a" (sp1),"=d" (sp2)); |
| } else { |
| sp1 = 521288629 + v->pass; |
| sp2 = 362436069 - v->pass; |
| } |
| rand_seed(sp1, sp2, 0); |
| } |
| |
| s_barrier(); |
| for (i=0; i < c_iter; i++) { |
| if (my_cpu == mstr_cpu) { |
| sp1 = rand(0); |
| sp2 = ~p1; |
| } |
| s_barrier(); |
| movinv1(2,sp1,sp2, my_cpu); |
| BAILOUT; |
| } |
| break; |
| |
| case 7: /* Block move (test #7) */ |
| block_move(c_iter, my_cpu); |
| 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_cpu); |
| BAILOUT |
| s_barrier(); |
| movinv32(c_iter,~p1, 0xfffffffe, |
| 0x7fffffff, 1, i, my_cpu); |
| BAILOUT |
| } |
| break; |
| |
| case 9: /* Random Data Sequence (test #9) */ |
| for (i=0; i < c_iter; i++) { |
| s_barrier(); |
| movinvr(my_cpu); |
| 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_cpu); |
| BAILOUT |
| |
| /* Switch patterns */ |
| s_barrier(); |
| modtst(i, 2, p2, p1, my_cpu); |
| 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_cpu); |
| bitf_sleep = 1; |
| break; |
| case 1: /* Sleep for the specified time */ |
| /* Only sleep once */ |
| if (bitf_sleep) { |
| sleep(c_iter, 1, my_cpu); |
| bitf_sleep = 0; |
| } |
| break; |
| case 2: /* Now check all of memory for changes */ |
| bit_fade_chk(0, my_cpu); |
| break; |
| case 3: /* Fill all of memory 1's */ |
| bit_fade_fill(-1, my_cpu); |
| bitf_sleep = 1; |
| break; |
| case 4: /* Sleep for the specified time */ |
| /* Only sleep once */ |
| if (bitf_sleep) { |
| sleep(c_iter, 1, my_cpu); |
| bitf_sleep = 0; |
| } |
| break; |
| case 5: /* Now check all of memory for changes */ |
| bit_fade_chk(-1, my_cpu); |
| 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_cpu); |
| BAILOUT |
| |
| /* Switch patterns */ |
| p2 = p1; |
| p1 = ~p2; |
| modtst(i, c_iter, p1,p2, my_cpu); |
| 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_cpu); |
| BAILOUT |
| |
| /* Switch patterns */ |
| p2 = p1; |
| p1 = ~p2; |
| modtst(i, c_iter, p1, p2, my_cpu); |
| BAILOUT |
| } |
| } |
| break; |
| } |
| return(0); |
| } |
| |
| /* Compute number of SPINSZ chunks being tested */ |
| int find_chunks(int tst) |
| { |
| int i, j, sg, wmax, ch; |
| struct pmap twin={0,0}; |
| unsigned long wnxt = TWO_GB; |
| unsigned long len; |
| |
| wmax = MAX_MEM/TWO_GB+2; /* The number of 2 GB segments +2 */ |
| /* Compute the number of SPINSZ 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 = TWO_GB; |
| } |
| |
| /* For all other windows */ |
| if (j > 1) { |
| twin.start = wnxt; |
| wnxt += TWO_GB; |
| twin.end = wnxt; |
| } |
| |
| /* Find the memory areas I am going to test */ |
| sg = compute_segments(twin); |
| for(i = 0; i < sg; i++) { |
| len = v->map[i].end - v->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 /= num_cpus; |
| break; |
| case 7: |
| case 8: |
| len /= (num_cpus & 0xe); |
| break; |
| } |
| } |
| ch += (len + SPINSZ -1)/SPINSZ; |
| } |
| } |
| return(ch); |
| } |
| |
| /* Compute the total number of ticks per pass */ |
| void find_ticks_for_pass(void) |
| { |
| int i; |
| |
| v->pptr = 0; |
| v->pass_ticks=0; |
| if (v->testsel >= 0) { |
| v->pass_ticks = find_ticks_for_test(v->testsel); |
| } else { |
| for (i=0; i<DEFTESTS; i++) { |
| /* Skip tests 2 and 4 if we are using 1 cpu */ |
| if (num_cpus == 1 && (i == 2 || i == 4)) { |
| continue; |
| } |
| v->pass_ticks += find_ticks_for_test(i); |
| } |
| } |
| } |
| |
| static int find_ticks_for_test(int tst) |
| { |
| int ticks=0, c, ch; |
| |
| /* Determine the number of chunks for this test */ |
| ch = find_chunks(tst); |
| |
| /* Set the number of iterations. We only do 1/3 of the iterations */ |
| /* on the first pass */ |
| if (v->pass == 0) { |
| c = tseq[tst].iter/3; |
| } else { |
| c = tseq[tst].iter; |
| } |
| |
| switch(tseq[tst].pat) { |
| case 0: /* Address test, walking ones */ |
| ticks = 2; |
| break; |
| case 1: /* Address test, own address */ |
| case 2: |
| ticks = 2; |
| break; |
| case 3: /* Moving inversions, all ones and zeros */ |
| case 4: |
| ticks = 2 + 4 * c; |
| break; |
| case 5: /* Moving inversions, 8 bit walking ones and zeros */ |
| ticks = 24 + 24 * c; |
| break; |
| case 6: /* Random Data */ |
| ticks = c + 4 * c; |
| break; |
| case 7: /* Block move */ |
| ticks = (ch + ch/num_cpus + c*ch); |
| break; |
| case 8: /* Moving inversions, 32 bit shifting pattern */ |
| ticks = (1 + c * 2) * 64; |
| break; |
| case 9: /* Random Data Sequence */ |
| ticks = 3 * c; |
| break; |
| case 10: /* Modulo 20 check, Random pattern */ |
| ticks = 4 * 40 * c; |
| break; |
| case 11: /* Bit fade test */ |
| ticks = c * 2 + 4 * ch; |
| break; |
| case 90: /* Modulo 20 check, all ones and zeros (unused) */ |
| ticks = (2 + c) * 40; |
| break; |
| case 91: /* Modulo 20 check, 8 bit pattern (unused) */ |
| ticks = (2 + c) * 40 * 8; |
| break; |
| } |
| if (cpu_mode == CPM_SEQ || tseq[tst].cpu_sel == -1) { |
| ticks *= num_cpus; |
| } |
| if (tseq[tst].pat == 7 || tseq[tst].pat == 11) { |
| return ticks; |
| } |
| return ticks*ch; |
| } |
| |
| static int compute_segments(struct pmap win) |
| { |
| 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 < v->plim_lower) { |
| wstart = v->plim_lower; |
| } |
| if (wend > v->plim_upper) { |
| wend = v->plim_upper; |
| } |
| if (wstart >= wend) { |
| return(0); |
| } |
| /* List the segments being tested */ |
| for (i=0; i< v->msegs; i++) { |
| unsigned long start, end; |
| start = v->pmap[i].start; |
| end = v->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, v->plim_lower); |
| cprint(LINE_SCROLL+(2*i), 54, ", "); |
| hprint(LINE_SCROLL+(2*i), 56, v->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, v->pmap[i].start); |
| cprint(LINE_SCROLL+(2*i+1), 32, ", "); |
| hprint(LINE_SCROLL+(2*i+1), 34, v->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)) { |
| v->map[sg].pbase_addr = start; |
| v->map[sg].start = mapping(start); |
| v->map[sg].end = emapping(end); |
| #if 0 |
| hprint(LINE_SCROLL+(sg+1), 0, sg); |
| hprint(LINE_SCROLL+(sg+1), 12, v->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); |
| } |
| |