mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-27 01:50:20 -06:00
16d55b7dad
This is no longer needed, now we can display as many CPUs as we can physically handle.
236 lines
8.7 KiB
C
236 lines
8.7 KiB
C
// 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 <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#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;
|
|
}
|