// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2022 Martin Whitaker. // // Derived from an extract of memtest86+ test.c: // // MemTest86+ V5 Specific code (GPL V2.0) // By Samuel DEMEULEMEESTER, sdemeule@memtest.org // http://www.canardpc.com - http://www.memtest.org // Thanks to Passmark for calculate_chunk() and various comments ! // ---------------------------------------------------- // test.c - MemTest-86 Version 3.4 // // Released under version 2 of the Gnu Public License. // By Chris Brady #include #include #include "display.h" #include "error.h" #include "test.h" #include "test_funcs.h" #include "test_helper.h" //------------------------------------------------------------------------------ // Public Functions //------------------------------------------------------------------------------ int test_block_move(int my_cpu, int iterations) { int ticks = 0; if (my_cpu == master_cpu) { display_test_pattern_name("block move"); } // Initialize memory with the initial pattern. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_cpu, i, 16 * sizeof(testword_t)); testword_t *p = start; testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_cpu < 0) { continue; } test_addr[my_cpu] = (uintptr_t)p; testword_t pattern1 = 1; do { testword_t pattern2 = ~pattern1; write_word(p + 0, pattern1); write_word(p + 1, pattern1); write_word(p + 2, pattern1); write_word(p + 3, pattern1); write_word(p + 4, pattern2); write_word(p + 5, pattern2); write_word(p + 6, pattern1); write_word(p + 7, pattern1); write_word(p + 8, pattern1); write_word(p + 9, pattern1); write_word(p + 10, pattern2); write_word(p + 11, pattern2); write_word(p + 12, pattern1); write_word(p + 13, pattern1); write_word(p + 14, pattern2); write_word(p + 15, pattern2); pattern1 = pattern1 << 1 | pattern1 >> (TESTWORD_WIDTH - 1); // rotate left } while (p <= (pe - 16) && (p += 16)); // test before increment in case pointer overflows do_tick(my_cpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } flush_caches(my_cpu); // Now move the data around. First move the data up half of the segment size // we are testing. Then move the data to the original location + 32 bytes. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_cpu, i, 16 * sizeof(testword_t)); testword_t *p = start; testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } size_t half_length = (pe - p + 1) / 2; testword_t *pm = p + half_length; for (int j = 0; j < iterations; j++) { ticks++; if (my_cpu < 0) { continue; } test_addr[my_cpu] = (uintptr_t)p; #ifdef __x86_64__ __asm__ __volatile__ ( "cld\n" "jmp L110\n\t" ".p2align 4,,7\n\t" "L110:\n\t" // At the end of all this // - the second half equals the initial value of the first half // - the first half is right shifted 64-bytes (with wrapping) // Move first half to second half "movq %1,%%rdi\n\t" // Destination, pm (mid point) "movq %0,%%rsi\n\t" // Source, p (start point) "movq %2,%%rcx\n\t" // Length, half_length (size of a half in DWORDS) "rep\n\t" "movsq\n\t" // Move the second half, less the last 64 bytes, to the first half, offset plus 64 bytes "movq %0,%%rdi\n\t" "addq $64,%%rdi\n\t" // Destination, p (start-point) plus 32 bytes "movq %1,%%rsi\n\t" // Source, pm (mid-point) "movq %2,%%rcx\n\t" "subq $8,%%rcx\n\t" // Length, half_length (size of a half in QWORDS) minus 8 QWORDS (64 bytes) "rep\n\t" "movsq\n\t" // Move last 8 QWORDS (64 bytes) of the second half to the start of the first half "movq %0,%%rdi\n\t" // Destination, p(start-point) // Source, 8 QWORDS from the end of the second half, left over by the last rep/movsl "movq $8,%%rcx\n\t" // Length, 8 QWORDS (64 bytes) "rep\n\t" "movsq\n\t" :: "g" (p), "g" (pm), "g" (half_length) : "rdi", "rsi", "rcx" ); #else __asm__ __volatile__ ( "cld\n" "jmp L110\n\t" ".p2align 4,,7\n\t" "L110:\n\t" // At the end of all this // - the second half equals the initial value of the first half // - the first half is right shifted 32 bytes (with wrapping) // Move first half to second half "movl %1,%%edi\n\t" // Destination, pm (mid point) "movl %0,%%esi\n\t" // Source, p (start point) "movl %2,%%ecx\n\t" // Length, half_length (size of a half in DWORDS) "rep\n\t" "movsl\n\t" // Move the second half, less the last 32 bytes, to the first half, offset plus 32 bytes "movl %0,%%edi\n\t" "addl $32,%%edi\n\t" // Destination, p (start-point) plus 32 bytes "movl %1,%%esi\n\t" // Source, pm (mid-point) "movl %2,%%ecx\n\t" "subl $8,%%ecx\n\t" // Length, half_length (size of a half in DWORDS) minus 8 DWORDS (32 bytes) "rep\n\t" "movsl\n\t" // Move last 8 DWORDS (32 bytes) of the second half to the start of the first half "movl %0,%%edi\n\t" // Destination, p(start-point) // Source, 8 DWORDS from the end of the second half, left over by the last rep/movsl "movl $8,%%ecx\n\t" // Length, 8 DWORDS (32 bytes) "rep\n\t" "movsl\n\t" :: "g" (p), "g" (pm), "g" (half_length) : "edi", "esi", "ecx" ); #endif do_tick(my_cpu); BAILOUT; } } while (!at_end && ++pe); // advance pe to next start point } flush_caches(my_cpu); // Now check the data. The error checking is rather crude. We just check that the // adjacent words are the same. for (int i = 0; i < vm_map_size; i++) { testword_t *start, *end; calculate_chunk(&start, &end, my_cpu, i, 16 * sizeof(testword_t)); testword_t *p = start; testword_t *pe = start; bool at_end = false; do { // take care to avoid pointer overflow if ((end - pe) >= SPIN_SIZE) { pe += SPIN_SIZE - 1; } else { at_end = true; pe = end; } ticks++; if (my_cpu < 0) { continue; } test_addr[my_cpu] = (uintptr_t)p; do { testword_t p0 = read_word(p + 0); testword_t p1 = read_word(p + 1); if (unlikely(p0 != p1)) { data_error(p, p0, p1, false); } } while (p <= (pe - 2) && (p += 2)); // test before increment in case pointer overflows do_tick(my_cpu); BAILOUT; } while (!at_end && ++pe); // advance pe to next start point } return ticks; }