/******************************************************************************* HWDD Memory module Copyright (c) 2011 Network Appliance, Inc. hwdd_memalgos.c: memory alogorithms *******************************************************************************/ #include #include #include "hwdd_mem.h" extern int max_pattern_length; #define MAX_NUM_WORDS_PER_COL 3 // three words are displayed per row in memory dump utility #define MAX_ROWS_PER_PAGE 14 // A page display includes fourteen rows static u64 hwdd_seed=0xdeadbeefdeadbeef; static void mem_error(u64 addr, u64 val_exp, u64 val_act, void* ptr); /************************************************************************ * hwdd_mem_randdata_test- * Algorithm as follows: * 1) store different random pattern in each location * 2) store complement of pattern in each location (toggles all bits) * 3) for each location, check for complement pattern and rewrite original * 4) check each location for original pattern * 5) repeat steps #1:4 three times * * This test is intended to verify all locations of SIMM memory and is * expected to detect all common DRAM failures such as stuck cells or * cell coupling. Intermittent errors may also be encountered, caused * by noise or margin problems. Parity and ECC are stressed by the * random patterns used. ********************************************************************* */ int hwdd_mem_randdata_test(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void *ptr) { u64 seed,save_seed,rand_value,value_read; u64 *wordloc,*endloc; u8 error_flag=0; unsigned long expire = SLEEP_AFTER; wordloc=(u64 *)virt_start; endloc=(u64 *)virt_end; seed = hwdd_seed; save_seed=seed; //TODO: having a loop (SLDIAG,not put in loop) /* step1, Writing random pattern*/ while (wordloc < endloc) { mix_random(seed, rand_value); *wordloc = rand_value; wordloc++; } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } /* step2, flipping the data */ seed=save_seed; // store initial value wordloc=(u64 *)virt_start; // start from beginning location while (wordloc < endloc) { mix_random(seed, rand_value); //TODO: read back the value and flipping the data *wordloc =~rand_value; wordloc++; } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } //step 3 seed=save_seed; // store initial value wordloc=(u64 *)virt_start; // start from beginning while (wordloc < endloc) { mix_random(seed, rand_value); if( (value_read=*wordloc) !=~rand_value){ mem_error(((u64)wordloc - virt_start + phys_start), rand_value, *wordloc, ptr); error_flag=1; } *wordloc=rand_value; // writing the original value wordloc++; } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } //step 4: check for original value seed=save_seed; // store initial value wordloc=(u64 *)virt_start; // start from beginning while (wordloc < endloc) { mix_random(seed, rand_value); if( (value_read=*wordloc) !=rand_value){ mem_error(((u64)wordloc - virt_start + phys_start), rand_value, *wordloc, ptr); error_flag=1; } wordloc++; } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } if( !error_flag ) { hwdd_seed=seed; //changing the global seed value return PASS; } return FAIL; } /* *mem_error - report error if expected value differs from read value */ static void mem_error(u64 addr, u64 val_exp, u64 val_act, void *ptr) { u64 val_diff; val_diff = val_exp ^ val_act; hwdd_printk(HWDD_ERROR, ptr, " ERROR Addr =%016Lx : Exp =%016Lx Act =%016Lx Diff =%016Lx\n",addr, val_exp, val_act, val_diff); } /* * fast_rand_rw() * routine to access memory at random addresses and flip data */ static int fast_rand_rw(register volatile u64 *baseloc,register u64 cap, register u64 seed) { register u32 i; register volatile u64* wordloc; u64 smask = 0, tmask = 0; /* * smask is the true full 1's mask of cap */ tmask = cap; while(tmask) { smask<<=1; smask |= 1; tmask>>=1; } i = 1000; while (i--) { mix_seed(seed); /* make a new pattern */ wordloc = baseloc + (MAXMASK((seed&smask),cap)); /* make address */ *wordloc = ~(*wordloc); /* flip data at loc */ } return seed; } /*********************************************************************** random address test * Algorithm as follows: * 1) store random pattern in each location * 2) for N times through loop: * 3) randomly generate valid address * 4) read address, complement pattern read and re-write * 5) check each location (sequentially) for pattern or complement * * This test is intended to verify overall operation of main memory, * with particular emphasis on noise, signal coupling and simultaneous * switching problems. Detection of intermittents and margin problems * is also expected. (Note that we don't worry about cache hits. The * few times we hit cache will just scramble the timing a little and * make the test that much more random.) **********************************************************************/ int hwdd_mem_randaddr_test(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { register volatile u64 *endloc, *wordloc; register u64 seed; register u64 cap; u64 val_read, val_exp, val_off,rand_value; register volatile u64 save_seed; u64 msize = virt_end - virt_start; u64 megs = bytes_to_mbytes(msize); u16 n,i; u8 error_flag=0 ; unsigned long expire = SLEEP_AFTER; wordloc = (u64*)virt_start; endloc = (u64*)virt_end; seed = hwdd_seed; save_seed = seed; //step1, storing random pattern in each location while (wordloc < endloc) { mix_random(seed, rand_value); *wordloc = rand_value; wordloc++; } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } cap = (msize >> 3) - 1; //maximum value, a start pointer can increment n = (megs / 16 + 7); /* set count based on size, mode */ while (n--) { seed = fast_rand_rw((u64*)virt_start, cap, seed); } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } /* we expect either pattern or compliment */ seed = save_seed; /* get starting seed again */ wordloc = (u64*)virt_start; /* now check every addr */ do { /* be sure correct pat */ mix_random(seed,val_exp); /* make a new pattern */ val_read = *wordloc; /* fetch data from mem */ if (val_read != val_exp && val_read != ~val_exp) { val_off = val_exp ^ val_read; /* compute difference */ i = 0; /* init diff counter */ while (val_off) { /* now count bits */ if ((val_off >>= 1) & 0x1) { i++; /* count 'em up */ } } val_off = val_exp; /* this value closer? */ if (i > 32) { /* which expected to use */ val_off = ~val_exp; /* way off, use other */ } mem_error(((u64)wordloc - virt_start + phys_start), val_off, val_read, ptr); error_flag=1; } } while (++wordloc < endloc); /* spin through all mem */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } if (!error_flag) { hwdd_seed = seed; return PASS; } return FAIL; // error detected } /* * hwdd_test_checker -Checker Board Memory Test * fills and verifies checker pattern */ int hwdd_test_checker(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { u64 *word_loc,*end_loc; unsigned long expire = SLEEP_AFTER; u8 error_flag = 0; word_loc = (u64 *)virt_start; end_loc = (u64 *)virt_end; /* fill given memory range with checker pattern */ while (word_loc < end_loc) { *word_loc = CHECKER_PATTERN; word_loc++; } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } /* verify_checker pattern*/ word_loc = (u64 *)virt_start; while (word_loc < end_loc) { if (*word_loc != CHECKER_PATTERN) { mem_error(((u64)word_loc - virt_start + phys_start), CHECKER_PATTERN, *word_loc, ptr); error_flag = 1; } word_loc++; } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } if (error_flag == 1) { return FAIL; } else{ return PASS; } } static int walk_check(u64 *location, u64 exp, u64 virt_start, u64 phys_start, void *ptr,u8 *error_flag_ptr) { u64 read_data; /* data read from memory */ read_data = *location; if (read_data != exp) { mem_error(((u64)location - virt_start + phys_start), exp, read_data, ptr); if (*error_flag_ptr == 0) { *error_flag_ptr = 1; } return FAIL; } return PASS; } /*********************************************************************** * mem_data_walk () * Performs a walking 1 in a field of zeroes and a walking 0 in a * field of ones. This test is repeated at each nMB "bank" boundary. * (Both walking ones and walking zeros are run across 256 bits of * memory space to ensure coverage of even the widest memory busses * used on Alpha platforms.) * * This test is intended to verify data lines within main memory * and to/from/within each SIMM bank. Expected to detect hard faults * such as shorts and opens. * if we spend more than SLEEP_FOR seconds, we will schedule() out **********************************************************************/ int hwdd_mem_data_walk(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { u64 *location, *end_loc; u64 data, exp; int i; unsigned long expire = SLEEP_AFTER; u8 error_flag = 0; u16 min_test_range = 0; location=(u64 *)virt_start; end_loc =(u64 *)virt_end; min_test_range = 32; if (((virt_end - virt_start) % min_test_range) != 0) { if ((virt_end - virt_start) < min_test_range) { hwdd_printk(HWDD_INFO, ptr, " Minimum testable range is %d bytes. Provide Multiple of %d bytes to test \n",min_test_range,min_test_range); return FAIL; } virt_end -= ((virt_end - virt_start) % min_test_range); hwdd_printk(HWDD_INFO, ptr, " Testable range should be %d bytes aligned. Aligning range to 0x%Lx \n",min_test_range,(virt_end - virt_start + phys_start)); } while (((u8 *)location + min_test_range) <= (u8 *)end_loc) { // checking whether next 32 bytes of memory is allocated or not data = 1; /* walk a one in a field of zeros */ for ( i = 1; i <= 256; i++) { // writing 256 bits or 32 bytes(4*8) *location++ = (i < 64) ? data : 0; *location++ = (i >= 64 && i < 128) ? data : 0; *location++ = (i >= 128 && i < 192) ? data : 0; *location = (i >= 192 && i < 256) ? data : 0; location -= 3; /* back our pointer up */ // start checking the written data exp = (i < 64) ? data : 0; walk_check(location, exp, virt_start, phys_start, ptr, &error_flag); /* read back and check */ exp = (i >= 64 && i < 128) ? data : 0; walk_check(++location, exp, virt_start, phys_start, ptr, &error_flag); exp = (i >= 128 && i < 192) ? data : 0; walk_check(++location, exp, virt_start, phys_start, ptr, &error_flag); exp = (i >= 192 && i < 256) ? data : 0; walk_check(++location, exp, virt_start, phys_start, ptr, &error_flag); data <<= 1; /* make next pattern */ if ((data & 0xffffffffffffffff) == 0) { data = 1; } location -= 3; } /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } //walk a zero in a field of ones data = 0xfffffffffffffffe; for ( i = 1; i <= 256; i++) { // writing 256 bits or 32 bytes(4*8) *location++ = (i < 64) ? data : 0xffffffffffffffff; *location++ = (i >= 64 && i < 128) ? data : 0xffffffffffffffff; *location++ = (i >= 128 && i < 192) ? data : 0xffffffffffffffff; *location = (i >= 192 && i < 256) ? data : 0xffffffffffffffff; location -=3; /* back our pointer up */ // start checking the written data exp = (i < 64) ? data : 0xffffffffffffffff; walk_check(location, exp, virt_start, phys_start, ptr, &error_flag); /* read back and check */ exp = (i >= 64 && i < 128) ? data : 0xffffffffffffffff; walk_check(++location, exp, virt_start, phys_start, ptr, &error_flag); exp = (i >= 128 && i < 192) ? data : 0xffffffffffffffff; walk_check(++location, exp, virt_start, phys_start, ptr, &error_flag); exp = (i >= 192 && i < 256) ? data : 0xffffffffffffffff; walk_check(++location, exp, virt_start, phys_start, ptr, &error_flag); data <<= 1; /* make next pattern */ data |= 1; // to make last bit 1 since, always last bit gets zero value if ((data & 0xffffffffffffffff) == 0xffffffffffffffff) { data =0xfffffffffffffffe ; } location -= 3; /* restore mem pointer */ } // end of for statement location += 4; // move to next 256 bits /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } if (error_flag == 1) { return FAIL; } else { return PASS; } } /*********************************************************************** * mem_addr_walk() * Algorithm as follows: * 1) store log of address in each power-of-two address * (place value into each byte so many bits get tested) * 2) check each power-of-two address for address pattern * * This test is intended to verify address lines within main memory * and to/from/within each DIMM bank. Expected to detect hard faults * such as shorts and opens. **********************************************************************/ int hwdd_mem_addr_walk(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { int i=0; u64 offset,val_read,val_exp; u64 *wordloc,*endloc,*baseloc; u64 expected_values[ ]={0x2142638421426384,0x1726354417263544,0xa3b2c1d0a3b2c1d0,0xb1a29384b1a29384}; u64 values_exp_add[ ]={0x0101010101010101,0x0404040404040404,0x0101010101010101,0x0202020202020202}; unsigned long expire = SLEEP_AFTER; u8 error_flag = 0; baseloc=(u64 *)virt_start; endloc=(u64 *)virt_end; for(i=0;i<4;i++){ wordloc = baseloc; val_exp =expected_values[i]; offset = 0x1; do { *wordloc = val_exp; /* store address pattern */ wordloc = baseloc + offset; /* point to next address */ val_exp += values_exp_add[i]; /* make next pattern */ offset <<= 1; /* make next address offset */ } while (wordloc < endloc && wordloc > baseloc); /* spin through all we can */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } // checking for the written data wordloc = baseloc; val_exp =expected_values[i]; offset = 0x1; do { if ((val_read = *wordloc) != val_exp) { /* is data what we want? */ mem_error(((u64)wordloc - virt_start + phys_start), val_exp, val_read, ptr); error_flag = 1; } wordloc = baseloc + offset; /* point to next address */ val_exp += values_exp_add[i]; /* make next pattern */ offset <<= 1; /* make next address offset */ } while (wordloc < endloc && wordloc > baseloc); /* spin through all we can */ /* return FAIL if user had issued a STOP */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } // end of for statement if (error_flag == 1) { return FAIL; } else { return PASS; } } /*********************************************************************** * mem_stuck_faults() * Algorithm as follows: * 1) store zero-bits to all of memory * 2) check for zero-bits in each location * 3) store one-bits in all of memory * 4) check for one-bits in each location * 5) store zero-bits to all of memory * 6) check for zero-bits in each location * * This test is intended to check each and every cell in DRAM for * stuck-at faults to ferret out gross errors quickly. An extra pass * is used to verify that both 0->1 and 1->0 transitions are tested. **********************************************************************/ int mem_stuck_faults(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { u64 *wordloc,*endloc; u64 val_read,val_exp ; unsigned long expire = SLEEP_AFTER; u8 error_flag =0; wordloc = (u64 *)virt_start; endloc = (u64 *)virt_end; // step 1, store zero-bits to all of memory while (wordloc < endloc) { *wordloc++ = 0x0000000000000000; /* all zero bits to mem */ } /* check for abort signal from the user and sleep for a while*/ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } // step 2,check for zero-bits in each location wordloc = (u64 *)virt_start; val_exp = 0x0000000000000000 ; do { if ((val_read = *wordloc) != val_exp) { /* is data what we want? */ mem_error(((u64)wordloc - virt_start + phys_start), val_exp, val_read, ptr); error_flag = 1; } } while (++wordloc < endloc); /* spin through all mem */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } //step 3, store one-bits in all of memory wordloc = (u64 *)virt_start; while (wordloc < endloc) { *wordloc++ = 0xffffffffffffffff; /* setting all bits */ } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } //step 4,check for one-bits in each location wordloc = (u64 *)virt_start; val_exp = 0xffffffffffffffff; do { if ((val_read = *wordloc) != val_exp) { /* is data what we want? */ mem_error(((u64)wordloc - virt_start + phys_start), val_exp, val_read, ptr); } } while (++wordloc < endloc); /* spin through all mem */ if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } //step 5, store zero-bits to all of memory wordloc = (u64 *)virt_start; while (wordloc < endloc) { *wordloc++ = 0x0000000000000000; /* all zero bits to mem */ } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } // step 6, check for zero-bits in each location wordloc = (u64 *)virt_start; val_exp = 0x0000000000000000 ; do { if ((val_read = *wordloc) != val_exp) { /* is data what we want? */ mem_error(((u64)wordloc - virt_start + phys_start), val_exp, val_read, ptr); } } while (++wordloc < endloc); return PASS; } /* function writes character array to memory (hex pattern format ) and returns the number of characters written ( 1 char will hold 2 hex values) */ int mem_chars_to_hex(const char* begin_str, char* to) { int digit,digit1,i = 0,len = 0; char *begin; len = strlen(begin_str); begin = (char *)kmalloc(len,GFP_ATOMIC); strcpy(begin,begin_str); while (len > 0) { digit = begin[--len]; if (digit >= '0' && digit <= '9') digit -= '0'; else if (digit >= 'a' && digit <= 'f') digit = digit - 'a' + 0xa; else if (digit >= 'A' && digit <= 'F') digit = digit - 'A' + 0xa; else { kfree(begin); return 0; } if (len <= 0) { digit1 = 0; //considering the case when odd length of hex pattern is given goto last; } digit1 = begin[--len]; if (digit1 >= '0' && digit1 <= '9') digit1 -= '0'; else if (digit1 >= 'a' && digit1 <= 'f') digit1 = digit1 - 'a' + 0xa; else if (digit1 >= 'A' && digit1 <= 'F') digit1 = digit1 - 'A' + 0xa; else { kfree(begin); return 0; } last: digit1 <<= 4; digit1 |= digit; *to++ = (char)digit1; i++; } kfree(begin); return i; } /* fill memory with data pattern */ int mem_fill_data(u64 virt_start, u64 virt_end, char pattern[], u64 phys_start, pid_t ppid, void* ptr) { u8 *charloc, *charend, *wordloc, *endloc; u16 count = 0; unsigned long expire = SLEEP_AFTER; wordloc = (u8 *)virt_start ; endloc = (u8 *)virt_end ; charloc = (u8 *)virt_start ; //write first instance of the pattern to memory // later memory to memory, copy the pattern (in below while loop) charend = charloc + (mem_chars_to_hex(pattern, wordloc)); if (charloc == charend) { return FAIL; } while (charend < endloc) { count++; *charend++ = *charloc++; if ((count % 10240) == 0) { // after every 10kb fill ,checking for stop signal and scheduling out for a momment if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } count = 0; } } return PASS; } int get_char(char ch ) { if (ch >= 10) { return ('a' + (ch - 10)); } else { return (ch + '0'); } } void memory_err(u8 *charend, u8 *charloc, u64 virt_start, u64 phys_start, void *ptr) { u8 ch1 = 0, ch2 = 0, ch3 =0,ch4 = 0; ch1 = get_char(((*charend & 0xf0) >> 4)); ch2 = get_char((*charend & 0x0f)); ch3 = get_char(((*charloc & 0xf0) >> 4)); ch4 = get_char((*charloc & 0x0f)); charend = ((charend - virt_start) + phys_start); // changing to physical address hwdd_printk(HWDD_ERROR, ptr, " ERROR at Addr =%016Lx : Act =%c%c Exp =%c%c \n",charend,ch1,ch2,ch3,ch4); return; } /* check memory with data pattern */ int mem_check_data(u64 virt_start, u64 virt_end, char user_pattern[], u64 phys_start, pid_t ppid, void* ptr) { u8 *charloc, *charend, *endloc; char char_count = 0,i; unsigned long expire = SLEEP_AFTER; char pattern[max_pattern_length]; //should use same MACRO defined memioctl.h strcpy(pattern,user_pattern); //user pattern will be changed ,so storing it in some buffer charend = (u8 *)virt_start; charloc = pattern; endloc = (u8 *)virt_end; char_count = mem_chars_to_hex(pattern,pattern); if (char_count == 0) { return FAIL; } //check for first instance of the pattern for(i = 0; i < char_count; i++) { if (*charend != *charloc) { memory_err(charend,charloc,virt_start,phys_start,ptr); return FAIL; } charend++; charloc++; } char_count = 0; charloc = (u8 *)virt_start; while (charend < endloc) { char_count++; if (*charend != *charloc) { memory_err(charend,charloc,virt_start,phys_start,ptr); return FAIL; // errors will be keep on happening since same pattern is written through out the given memory range, so returning fail here itself } charend++; charloc++; if ((char_count%10240) == 0) { //for every 10kb data check, sleep for some time and check for user stop signal if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } char_count = 0; } } return PASS; } /*********************************************************************** * mem_word_walk () * Performs a walking ffffffff word in a field of 64 words of zero and * a walking 00000000 word in a field of 64 words of ones. This test * is repeated at each 2MB "bank" boundary. * * This test is intended to verify switching and holding functions * within DRAM. **********************************************************************/ int mem_word_walk(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { register int i,n,max_words; u32 *location,*endloc,*wordloc; // keeping u32 pointer same as sysdiag code (word size -32bits ) u32 val_exp,val_read; unsigned long expire = SLEEP_AFTER; u16 min_test_range = 0; wordloc = (u32 *)virt_start ; //assuming word size as 4 byte (SYSDIAG code) max_words = 64; // sydiag code min_test_range = ((u64)(wordloc + max_words) - virt_start); if (((virt_end - virt_start) % min_test_range) != 0) { if ((virt_end - virt_start) < min_test_range) { hwdd_printk(HWDD_INFO, ptr, " Minimum testable range is %d bytes. Provide Multiple of %d bytes to test \n",min_test_range,min_test_range); return FAIL; } virt_end -= ((virt_end - virt_start) % min_test_range); hwdd_printk(HWDD_INFO, ptr, " Testable range should be %d bytes aligned. Aligning range to 0x%Lx \n",min_test_range,(virt_end - virt_start + phys_start)); } endloc = (u32 *)virt_end ; while ((wordloc + max_words) <= endloc) { for (n = 0; n < max_words; n++) { location = wordloc; for (i = 0; i < max_words; i++) { if (i == n) { /* special word in block? */ *location++ = 0xffffffff; /* store complement */ }else { *location++ = 0x0; /* store background data */ } } location = wordloc; for (i = 0; i < max_words; i++) { if (i == n) { /* special word in block? */ val_exp = 0xffffffff; /* expect complement */ }else { val_exp = 0x0; /* expect background data */ } val_read = *location++; if (val_read != val_exp) { mem_error(((u64)location - virt_start + phys_start), val_exp, val_read, ptr); } } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } for (n = 0; n < max_words; n++) { /* walk 0's in f's */ location = wordloc; for (i = 0; i < max_words; i++) { if (i == n) { /* special word in block? */ *location++ = 0x0; /* store complement */ }else { *location++ = 0xffffffff; /* store background data */ } } location = wordloc; for (i = 0; i < max_words; i++) { if (i == n) { /* special word in block? */ val_exp = 0x0; /* expect complement */ }else { val_exp = 0xffffffff; /* expect background data */ } val_read = *location++; if (val_read != val_exp) { mem_error(((u64)location - virt_start + phys_start), val_exp, val_read, ptr); } } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } wordloc = wordloc + max_words; } return PASS; } /*********************************************************************** *dump_memory_utility() * dumps the contents of a specified location * prints 3 words ( 1word is 8 bytes ) per line * address(location) is incremented by 3 ***********************************************************************/ int dump_memory_utility(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { u64 *start_addr; u8 num_rows = 0,remaining_byte = 0, chars_count = -1; char num_buff[18]; /* need 16 bytes to store 16 chars, one byte to store '\0' */ hwdd_printk(HWDD_INFO, ptr, "\n Location | Contents\n\n"); /* printing 8 bytes of data at a time */ for(start_addr = (u64 *)virt_start; (start_addr + MAX_NUM_WORDS_PER_COL) <= (u64 *)virt_end ; start_addr += MAX_NUM_WORDS_PER_COL) { /* Split in pages */ if (num_rows == MAX_ROWS_PER_PAGE) { /* End the current page of memory dump */ hwdd_printk(HWDD_INFO, ptr, "\n\n"); num_rows = 0; } hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %016Lx %016Lx %016Lx",(unsigned long long)phys_start, (unsigned long long)start_addr[2],(unsigned long long)start_addr[1],(unsigned long long)start_addr[0]); phys_start += (MAX_NUM_WORDS_PER_COL * sizeof(*start_addr)); ++num_rows; } if (((u64 *)virt_end - start_addr) >= (MAX_NUM_WORDS_PER_COL - 1)) { hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %016Lx %016Lx",(unsigned long long)phys_start, (unsigned long long)start_addr[1],(unsigned long long)start_addr[0]); start_addr += (MAX_NUM_WORDS_PER_COL - 1); phys_start += ((MAX_NUM_WORDS_PER_COL - 1) * sizeof(*start_addr)); // updating the physical address }else if (((u64 *)virt_end - start_addr) >= (MAX_NUM_WORDS_PER_COL - 2)) { hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %016Lx",phys_start, (unsigned long long)start_addr[0]); start_addr += (MAX_NUM_WORDS_PER_COL - 2); phys_start += ((MAX_NUM_WORDS_PER_COL - 2) * sizeof(*start_addr)); } remaining_byte = ((u8 *)virt_end - (u8 *)start_addr); if (remaining_byte > 0) { /* printing the location which are less than 8 bytes */ chars_count = sprintf(num_buff, "%016Lx",(unsigned long long)start_addr[0]); if (chars_count <= 0){ hwdd_printk(HWDD_ERROR, ptr, "\n ERROR:sprintf() failed \n"); return FAIL; } switch (remaining_byte) { case 1 : hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %s",phys_start, (num_buff + 14)); break; case 2 : hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %s",phys_start, (num_buff + 12)); break; case 3 : hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %s",phys_start, (num_buff + 10)); break; case 4 : hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %s",phys_start, (num_buff + 8)); break; case 5 : hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %s",phys_start, (num_buff + 6)); break; case 6 : hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %s",phys_start, (num_buff + 4)); break; case 7 : hwdd_printk(HWDD_INFO, ptr, " 0x%012Lx | %s",phys_start, (num_buff + 2)); break; default:break; } } return PASS; } /*********************************************************************** * mem_byte_pat () * Spins through all 256 patterns in each byte of a word at each * location, testing one byte at a time. * This test is intended to verify parity generation/check logic (and * ECC logic on Alpha, though not as exhaustively). **********************************************************************/ int mem_byte_pat(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { u32 *location,read_data; u16 i; unsigned long expire = SLEEP_AFTER; location = (u32 *)virt_start; //considering word size as 4 byte (sysdiag code) while(location < (u32 *)virt_end) { for (i = 0; i < 256; i++) { // stroing all possibility patterns *location = i << 24; /* store pattern (byte 3) */ read_data = *location; if (read_data != (i << 24)) { mem_error(((u64)location - virt_start + phys_start), i << 24, read_data, ptr); } if ((i % 10) == 0) { if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } } for (i = 0; i < 256; i++) { *location = i << 16; /* store pattern (byte 2) */ read_data = *location; if (read_data != (i << 16)) { mem_error(((u64)location - virt_start + phys_start), i << 16, read_data, ptr); } if ((i % 10) == 0) { if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } } for (i = 0; i < 256; i++) { *location = i << 8; /* store pattern (byte 1) */ read_data = *location; if (read_data != (i << 8)) { mem_error(((u64)location - virt_start + phys_start), i << 8, read_data, ptr); } if ((i % 10) == 0) { if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } } for (i = 0; i < 256; i++) { *location = i; /* store pattern (byte 0) */ read_data = *location; if (read_data != i) { mem_error(((u64)location - virt_start + phys_start), i, read_data, ptr); } if ((i % 10) == 0) { if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } } location++; } return PASS; } /*********************************************************************** * mem_partial () * Another test of partial-word stores and loads, this test does a * series of byte and short stores interspersed with word stores, * followed by a series of loads and compares. * * This is intended to verify correct behavior of partial word merge * logic and to check PC memory ability to handle partial word * operations (Alpha does data merging in the CPU). This is a * check primarily of the control logic to deal with partial word * operations to/from memory. **********************************************************************/ int mem_partial(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { typedef struct partial_words{ /* structure for partial word ops */ u16 s1; u16 s2; u32 w1; u32 w2; u8 b1; u8 b2; u16 s3; u32 w3; u32 w4; } partial_words_t; u8 i, c = 2; register partial_words_t *pp,*bb; /* struct pointer for partial ops */ unsigned long expire = SLEEP_AFTER; u16 min_test_range = 0; partial_words_t partial_words_pattern[2] = {{0x1aa1, 0x2aa2, 0xfffffff1, 0x0, 0xb1, 0xb2, 0x3aa3, 0x0, 0xfffffff4}, {0xa11a, 0xbabe, 0xfacade12, 0x0, 0x66, 0xbb, 0xdead, 0x0, 0xefface89}}; min_test_range = sizeof(partial_words_t); if (((virt_end - virt_start) % min_test_range) != 0) { if ((virt_end - virt_start) < min_test_range) { hwdd_printk(HWDD_INFO, ptr, " Minimum testable range is %d bytes. Provide Multiple of %d bytes to test \n",min_test_range,min_test_range); return FAIL; } virt_end -= ((virt_end - virt_start) % min_test_range); hwdd_printk(HWDD_INFO, ptr, " Testable range should be %d bytes aligned. Aligning range to 0x%Lx \n",min_test_range,(virt_end - virt_start + phys_start)); } while (c-- > 0) { bb = (partial_words_t *)virt_start; pp = bb; // while(((u8 *)bb + sizeof(partial_words_t)) <= ((u8 *)(partial_words_t *)virt_end)) { /* fill memory block */ while(((u8 *)bb + sizeof(partial_words_t)) <= (u8 *)virt_end) { for (i = 0; (i < 500 && (((u8 *)bb + sizeof(partial_words_t)) <= (u8 *)virt_end)); i++) { bb->s1 = partial_words_pattern[c].s1; bb->s2 = partial_words_pattern[c].s2; bb->w1 = partial_words_pattern[c].w1; bb->w2 = i; bb->b1 = partial_words_pattern[c].b1; bb->b2 = partial_words_pattern[c].b2; bb->s3 = partial_words_pattern[c].s3; bb->w3 = i; bb->w4 = partial_words_pattern[c].w4; bb++; } for (i = 0; (i < 500 && (((u8 *)pp + sizeof(partial_words_t)) <= (u8 *)virt_end)); i++) { if (pp->s2 != partial_words_pattern[c].s2) { mem_error(((u64)(&pp->s2) - virt_start + phys_start), partial_words_pattern[c].s2, pp->s2, ptr); } if (pp->b1 != partial_words_pattern[c].b1) { mem_error(((u64)(&pp->b1) - virt_start + phys_start), partial_words_pattern[c].b1, pp->b1, ptr); } if (pp->w1 != partial_words_pattern[c].w1) { mem_error(((u64)(&pp->w1) - virt_start + phys_start), partial_words_pattern[c].w1, pp->w1, ptr); } if (pp->s3 != partial_words_pattern[c].s3) { mem_error(((u64)(&pp->s3) - virt_start + phys_start), partial_words_pattern[c].s3, pp->s3, ptr); } if (pp->b2 != partial_words_pattern[c].b2) { mem_error(((u64)(&pp->b2) - virt_start + phys_start), partial_words_pattern[c].b2, pp->b2, ptr); } if (pp->s1 != partial_words_pattern[c].s1) { mem_error(((u64)(&pp->s1) - virt_start + phys_start), partial_words_pattern[c].s1, pp->s1, ptr); } if (pp->w2 != i) { mem_error(((u64)(&pp->w2) - virt_start + phys_start), i, pp->w2, ptr); } if (pp->w4 != partial_words_pattern[c].w4) { mem_error(((u64)(&pp->w4) - virt_start + phys_start), partial_words_pattern[c].w4, pp->w4, ptr); } if (pp->w3 != i) { mem_error(((u64)(&pp->w3) - virt_start + phys_start), i, pp->w3, ptr); } pp++; } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } // end of while } return PASS; } /*********************************************************************** * mem_alt_addr() * Algorithm as follows: * 1) generate two random patterns (pat1, pat2) * 2) store pat1/~pat1 into alternating *even* words in block * 3) check pat1/~pat1, store pat2/~pat2 into alternate *odd* words * 4) generate a new pat1 * 5) check pat2/~pat2, store pat1/~pat1 into alternate *even* words * 6) check new pat1/~pat1 * 7) check pat2/~pat2 * 8) repeat 1:7 twice more * * **Note: Addresses are counted down from high-to-low within the * test block. This seems to cause a lot more trouble for weak parts. * * This test is intended to stress memory switching between banks, * and especially byte marks and timing on PC systems. There is a * known design error on PC systems, where a fully-loaded memory * places too many loads on drivers. Any weakness at all in the SIMMs * can result in corrupted data or parity errors. This test is by far * the best one in the suite to exercise this problem. **********************************************************************/ int mem_alt_addr(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { u64 pattern1, pattern2, seed, val_read, pattern1_save, pattern2_save; u64 *endloc, *wordloc, *baseloc; u8 i = 2; unsigned long expire = SLEEP_AFTER; seed = hwdd_seed; /* pick up random seed */ baseloc = (u64 *)virt_start; endloc = (u64 *)virt_end; while (i-- > 0) { mix_random(seed,pattern1); /* make a new pattern */ mix_random(seed,pattern2); /* make a new pattern */ /* patterns can change based on odd or even occurances.so saving these patterns */ pattern1_save = pattern1; pattern2_save = pattern2; wordloc = endloc - 2; /* point to block end */ while (wordloc >= baseloc) { /* spin through the block */ *(wordloc + 1) = pattern1; /* store value to memory */ pattern1 = ~pattern1; /* flip all the bits */ wordloc -= 2; /* advance by two locations */ } wordloc = endloc - 2; /* point to end again */ pattern1 = pattern1_save; while (wordloc >= baseloc) { if ((val_read = *(wordloc + 1)) != pattern1) { mem_error(((u64)(wordloc + 1) - virt_start + phys_start), pattern1, val_read, ptr); } *wordloc = pattern2; /* store alternate pattern */ pattern1 = ~pattern1; /* flip all the bits */ pattern2 = ~pattern2; /* flip all the bits */ wordloc -= 2; /* advance by two locations */ } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } mix_random(seed,pattern1); /* make a new pattern */ wordloc = endloc - 2; /* point to block end again */ pattern2 = pattern2_save; pattern1 = pattern1_save; while (wordloc >= baseloc) { if ((val_read = *wordloc) != pattern2) { mem_error(((u64)wordloc - virt_start + phys_start), pattern2, val_read, ptr); } *(wordloc + 1) = pattern1; pattern1 = ~pattern1; /* flip all the bits */ pattern2 = ~pattern2; wordloc -= 2; /* advance by two locations */ } wordloc = endloc - 2; /* point to block end again */ pattern1 = pattern1_save; while (wordloc >= baseloc) { if ((val_read = *(wordloc + 1)) != pattern1) { mem_error(((u64)(wordloc + 1) - virt_start + phys_start), pattern1, val_read, ptr); } pattern1 = ~pattern1; wordloc -= 2; /* advance by two locations */ } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = endloc - 2; /* point to base again */ pattern2 = pattern2_save; while (wordloc >= baseloc) { if ((val_read = *wordloc) != pattern2) { mem_error(((u64)wordloc - virt_start + phys_start), pattern2, val_read, ptr); } pattern2 = ~pattern2; wordloc -= 2; /* advance by two locations */ } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } return PASS; } /*********************************************************************** * mem_parity() * Algorithm as follows: * 1) for walking ones across bytes: * 2) store all ones to odd (4,c,14,1c...) words * 3) check for ones in odd words, store pattern into even words * 4) store pattern into odd words * 5) check for pattern in odd words, store all ones into even words * 6) repeat 1:5 using walking zeroes across bytes (and all zeroes) * * **Note: Addresses are counted down from high-to-low within the * test block. This seems to cause a lot more trouble to weak parts. * * This test is intended to exercise PC parity bits and to stress memory * switching between banks, and byte marks on PC systems. **********************************************************************/ int mem_parity(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { u64 val_read,pattern,val_exp; u64 *endloc,*wordloc,*baseloc; u8 i; unsigned long expire = SLEEP_AFTER; baseloc = (u64 *)virt_start; endloc = (u64 *)virt_end; pattern = 0x0101010101010101; /* parity=ff pattern */ for (i = 0; i < 8; i++ ) { wordloc = endloc - 1; /* point to block end */ while (wordloc >= baseloc) { /* spin through the block */ *wordloc = 0xffffffffffffffff; /* store ones (parity=00) */ wordloc -= 2; /* advance by two locations */ } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = endloc - 1; /* point to end again */ val_exp = 0xffffffffffffffff ; while ((wordloc - 1) >= baseloc) { if ((val_read = *wordloc) != val_exp) { mem_error(((u64)wordloc - virt_start + phys_start), val_exp, val_read, ptr); } *(wordloc - 1) = pattern; /* store pattern (parity=ff) */ wordloc -= 2; /* advance by two locations */ } wordloc = endloc - 1; /* point to block end */ while (wordloc >= baseloc) { /* spin through the block */ *wordloc = pattern; /* store pattern to memory */ wordloc -= 2; /* advance by two locations */ } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = endloc - 1; /* point to end again */ while ((wordloc - 1) >= baseloc) { if ((val_read = *wordloc) != pattern) { mem_error(((u64)wordloc - virt_start + phys_start), pattern, val_read, ptr); } *(wordloc - 1) = 0xffffffffffffffff; /* store all ones */ wordloc -= 2; /* advance by two locations */ } pattern <<= 1; /* shift pattern to new bit */ } pattern = 0xfefefefefefefefe; /* parity=ff pattern */ for (i = 0; i < 8; i++ ) { wordloc = endloc - 1; /* point to block end */ while (wordloc >= baseloc) { *wordloc = 0x0000000000000000; /* store zeroes (parity=00) */ wordloc -= 2; /* advance by two locations */ } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = endloc - 1; /* point to end again */ val_exp = 0x0000000000000000 ; while ((wordloc - 1) >= baseloc) { if ((val_read = *wordloc) != val_exp) { mem_error(((u64)wordloc - virt_start + phys_start), val_exp, val_read, ptr); } *(wordloc - 1) = pattern; /* store pattern (parity=ff) */ wordloc -= 2; /* advance by two locations */ } wordloc = endloc - 1; /* point to block end */ while (wordloc >= baseloc) { /* spin through the block */ *wordloc = pattern; /* store pattern to memory */ wordloc -= 2; /* advance by two locations */ } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = endloc - 1; /* point to end again */ while ((wordloc - 1) >= baseloc) { if ((val_read = *wordloc) != pattern) { mem_error(((u64)wordloc - virt_start + phys_start), pattern, val_read, ptr); } *(wordloc - 1) = 0x0000000000000000; /* store zeroes */ wordloc -= 2; /* advance by two locations */ } pattern <<= 1; /* shift pattern to new bit */ pattern |= 1; } return PASS; } /*********************************************************************** * mem_byte_walk () * Performs a walking ff byte in a field of 256 bytes of zero and * a walking 00 byte in a field of 256 bytes of ones. This test * is repeated at each 2MB "bank" boundary. In addition to the * walking zeroes and ones, a similar pair of tests is run with a * complement byte stored at every *third* byte. * * This test is intended to check PC memory ability to handle partial * word operations (Alpha does data merging in the CPU). This is a * check primarily of the control logic to deal with partial word * operations to/from memory. **********************************************************************/ int mem_byte_walk(u64 virt_start, u64 virt_end, u64 phys_start, pid_t ppid, void* ptr) { register int i,n,max_bytes; u8 *endloc,*wordloc,*location; u64 val_exp,val_read; unsigned long expire = SLEEP_AFTER; u16 min_test_range = 0; wordloc = (u8 *)virt_start ; endloc = (u8 *)virt_end ; max_bytes = 256; min_test_range = ((u64)(wordloc + max_bytes) - virt_start); if (((virt_end - virt_start) % min_test_range) != 0) { if ((virt_end - virt_start) < min_test_range) { hwdd_printk(HWDD_INFO, ptr, " Minimum testable range is %d bytes. Provide Multiple of %d bytes to test \n",min_test_range,min_test_range); return FAIL; } virt_end -= ((virt_end - virt_start) % min_test_range); hwdd_printk(HWDD_INFO, ptr, " Testable range should be %d bytes aligned. Aligning range to 0x%Lx \n",min_test_range,(virt_end - virt_start + phys_start)); } while ((wordloc + max_bytes) <= endloc) { for (n = 0; n < max_bytes; n++) { location = wordloc; for (i = 0; i < max_bytes; i++) { if (i == n) { /* special word in block? */ *location++ = 0xff; /* store complement */ }else { *location++ = 0x0; } } location = wordloc; for (i = 0; i < max_bytes; i++) { if (i == n) { /* special word in block? */ val_exp = 0xff; /* expect complement */ }else { val_exp = 0x0; } val_read = *location++; if (val_read != val_exp) { mem_error(((u64)location - virt_start + phys_start), val_exp, val_read, ptr); } } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } for (n = 0; n < max_bytes; n++) { /* walk 0's in f's */ location = wordloc; for (i = 0; i < max_bytes; i++) { if (i == n) { /* special word in block? */ *location++ = 0x0; /* store complement */ }else { *location++ = 0xff; /* store background data */ } } location = wordloc; for (i = 0; i < max_bytes; i++) { if (i == n) { /* special word in block? */ val_exp = 0x0; /* expect complement */ }else { val_exp = 0xff; /* expect background data */ } val_read = *location++; if (val_read != val_exp) { mem_error(((u64)location - virt_start + phys_start), val_exp, val_read, ptr); } } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } for (n = 0; n < max_bytes; n++) { location = wordloc; for (i = 0; i < max_bytes; i++) { if (i == n) { /* special word in block? */ *location++ = 0xfd; /* store complement */ }else { *location++ = 0x0; } } location = wordloc; for (i = 0; i < max_bytes; i++) { if (i == n) { /* special word in block? */ val_exp = 0xfd; /* expect complement */ }else { val_exp = 0x0; } val_read = *location++; if (val_read != val_exp) { mem_error(((u64)location - virt_start + phys_start), val_exp, val_read, ptr); } } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } for (n = 0; n < max_bytes; n++) { /* walk 0's in f's */ location = wordloc; for (i = 0; i < max_bytes; i++) { if (i == n) { /* special word in block? */ *location++ = 0x0; /* store complement */ }else { *location++ = 0xfd; /* store background data */ } } location = wordloc; for (i = 0; i < max_bytes; i++) { if (i == n) { /* special word in block? */ val_exp = 0x0; /* expect complement */ }else { val_exp = 0xfd; /* expect background data */ } val_read = *location++; if (val_read != val_exp) { mem_error(((u64)location - virt_start + phys_start), val_exp, val_read, ptr); } } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } } location = wordloc; for (i = 0; i < max_bytes; i++) { *location++ = 0x0; } location = wordloc; for (i = 0; i < max_bytes; i += 3) { *location = 0xff; location += 3; } location = wordloc; for (i = 0; i < max_bytes; i++) { if (i % 3) { val_exp = 0x0; }else { val_exp = 0xff; } val_read = *location++; if (val_read != val_exp) { mem_error(((u64)location - virt_start + phys_start), val_exp, val_read, ptr); } } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } location = wordloc; for (i = 0; i < max_bytes; i++) { *location++ = 0x0; } location = wordloc; for (i = 0; i < max_bytes; i += 3) { *location = 0xdf; location += 3; } location = wordloc; for (i = 0; i < max_bytes; i++) { if (i%3) { val_exp = 0x0; }else { val_exp = 0xdf; } val_read = *location++; if (val_read != val_exp) { mem_error(((u64)location - virt_start + phys_start), val_exp, val_read, ptr); } } if (hwdd_sleep_checkstop(&expire, ppid)) { return FAIL; } wordloc = wordloc + max_bytes; } return PASS; } /* ***************************************************************************** */