mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2026-08-08 12:08:03 -05:00
* Initial commit for ARM64 (header.S & startup.S) * Add Aarch64 interrupt handler * Adapt lib/ to Aarch64 * Small ASM addition for AArch64 * Rewrite Aarch64 system/ - cpuid.c/cpuinfo.c for CPU detection (Qualcomm, Apple, Nvidia, etc) - Add ARM Power State Coordination Interface (PSCI) functions - vmem.c for Virtual Memory MAnagement on ARM64 - Various functions placeholders * Add pseudo-TSC correction for Aarch64 * Add pseudo-tsc measurement for Aarch64 * Add Aarch64 if/elif on basic IO/MMIO/MEM RW functions * Add support for Surface 13 / Snapdragon X KB USB Fix an issue with the XHCI.c drivers with packets > 8 bytes * Add MMIO Serial support for ARM64 * Add ARM64 ASM for block_move & mov_inv_fixed. Fix VM start pointer * Add ARM Serial MMIO configuration * Revert previous commit & reintroduce ARM64 cache functions * Add ACPI MCFG Table description & SMP parsing. This requires careful review/testing to avoid BC issues! * Revert previous commmit and reintroduce relocation and heap change for ARM64 * ARM64 does not support CPU PNS. Use DMI type4 parsing instead. Add SMBIOS v3 support (ARM64 does not support v2) * Fix typo in pci.h * Move Aarch64 load-limit computation from main.c to pmem.c * Add .arm64 to version string. Use PMU for clock computation display. Fix D5 Temp NULL not handled * Add Makefile and linker scripts for aarch64. Set cross-compiler from x86 by default * Attempt to add aarch64 to workflow * Remove a remaining internal debug flag * Correct typos * Fix ARM64 SMP freeze assign heap when RAM is one contiguous region, keep it below 3GB for DMA (RPi5), add DSB before SEV to avoid missed barrier wakeups Sync I-cache with D-cache after program relocation to prevent stale instruction fetch * Validate MADT entry lengths to avoid reading past the table (or looping forever) on malformed ACPI tables * aarch64: add NEON kernels for the vector PRSG tests Tests 3 (bus stress) and 5 (moving inversions, random) now dispatch to 128-bit NEON fill/scan instead of scalar. * Fix ARM64 data abort from the per-second DMI check & fix unbounded strstr() * Fix ARM64 data abort when saving results to USB after a relocation * Update README.md with AArch64 (ARM64) support * aarch64: shrink static string tables, harden MCFG/paging Replace pointer-based name tables with inline char arrays (saves ~1.5KB + relocs) alidate MCFG length/bus ranges, and bound pm_map/vm_map fills against overflows * Add NVIDIA & latest Apple ARM SoC to cpuid.c * Fix stale pointers read by the report writer after a program relocation cpu_model gets a static init (creates its reloc record)SPD type becomes an inline char array
295 lines
9.6 KiB
C
295 lines
9.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (C) 2004-2026 Sam Demeulemeester.
|
|
//
|
|
// Vector-wide pseudo-random sequence primitives shared by the memory tests.
|
|
//
|
|
// Memory is filled and checked with per-lane xorshift streams, one whole
|
|
// vector block at a time. On x86_64 the work is dispatched at runtime to
|
|
// AVX2 or SSE2 kernels with non-temporal stores (tests/x86/vec_prsg_*.c);
|
|
// other builds use the scalar kernels below, which implement the same
|
|
// algorithm. The check functions regenerate the sequence instead of storing
|
|
// it: xorshift is an invertible linear map, so the descending check simply
|
|
// steps the lane states backwards.
|
|
//
|
|
// SIMD dispatch approach inspired by earlier experimental work by
|
|
// Lionel Debroux.
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "simd.h"
|
|
|
|
#include "error.h"
|
|
#include "test.h"
|
|
|
|
#include "test_helper.h"
|
|
|
|
#include "vec_prsg.h"
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Constants
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Arbitrary distinct constants used to decorrelate the per-lane xorshift
|
|
// streams seeded from a single per-round seed.
|
|
#if TESTWORD_WIDTH > 32
|
|
static const testword_t lane_salt[VEC_LANES] = {
|
|
UINT64_C(0x9e3779b97f4a7c15),
|
|
UINT64_C(0xbf58476d1ce4e5b9),
|
|
UINT64_C(0x94d049bb133111eb),
|
|
UINT64_C(0x2545f4914f6cdd1d)
|
|
};
|
|
#else
|
|
static const testword_t lane_salt[VEC_LANES] = {
|
|
0x9e3779b9,
|
|
0xbf58476d,
|
|
0x94d049bb,
|
|
0x2545f491
|
|
};
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Inverts one y = x ^ (x << shift) step of the xorshift generator by repeated
|
|
// squaring, doubling the shift until it exceeds the word width.
|
|
static inline testword_t prsg_undo_lshift(testword_t y, int shift)
|
|
{
|
|
for (int k = shift; k < TESTWORD_WIDTH; k *= 2) {
|
|
y ^= y << k;
|
|
}
|
|
return y;
|
|
}
|
|
|
|
// Inverts one y = x ^ (x >> shift) step of the xorshift generator by repeated
|
|
// squaring, doubling the shift until it exceeds the word width.
|
|
static inline testword_t prsg_undo_rshift(testword_t y, int shift)
|
|
{
|
|
for (int k = shift; k < TESTWORD_WIDTH; k *= 2) {
|
|
y ^= y >> k;
|
|
}
|
|
return y;
|
|
}
|
|
|
|
// Returns the previous word in the pseudo-random sequence generated by prsg().
|
|
static inline testword_t prsg_inverse(testword_t state)
|
|
{
|
|
#if TESTWORD_WIDTH > 32
|
|
state = prsg_undo_lshift(state, 17);
|
|
state = prsg_undo_rshift(state, 7);
|
|
state = prsg_undo_lshift(state, 13);
|
|
#else
|
|
state = prsg_undo_lshift(state, 5);
|
|
state = prsg_undo_rshift(state, 17);
|
|
state = prsg_undo_lshift(state, 13);
|
|
#endif
|
|
return state;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Scalar kernels (all architectures)
|
|
//
|
|
// These process nblocks whole vector blocks starting at p (the descending
|
|
// check processes them from the top down). The lane states are carried in
|
|
// memory between invocations, so no SIMD state is live across calls to
|
|
// data_error(), do_tick(), or the thread barriers.
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void vec_fill_scalar(vec_state_t *st, testword_t *p, size_t nblocks, bool splat)
|
|
{
|
|
for (size_t k = 0; k < nblocks; k++) {
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
write_word(&p[l], st->lane[l]);
|
|
}
|
|
p += VEC_LANES;
|
|
if (!splat) {
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
st->lane[l] = prsg(st->lane[l]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void vec_check_fwd_scalar(vec_state_t *st, testword_t *p, size_t nblocks, bool splat, bool use_for_badram)
|
|
{
|
|
for (size_t k = 0; k < nblocks; k++) {
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
testword_t expect = st->lane[l];
|
|
testword_t actual = read_word(&p[l]);
|
|
if (unlikely(actual != expect)) {
|
|
data_error(&p[l], expect, actual, use_for_badram);
|
|
}
|
|
write_word(&p[l], ~expect);
|
|
}
|
|
p += VEC_LANES;
|
|
if (!splat) {
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
st->lane[l] = prsg(st->lane[l]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void vec_check_rev_scalar(vec_state_t *st, testword_t *p, size_t nblocks, bool splat, bool use_for_badram)
|
|
{
|
|
for (size_t k = nblocks; k-- > 0; ) {
|
|
testword_t *q = p + k * VEC_LANES;
|
|
if (!splat) {
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
st->lane[l] = prsg_inverse(st->lane[l]);
|
|
}
|
|
}
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
testword_t expect = ~st->lane[l];
|
|
testword_t actual = read_word(&q[l]);
|
|
if (unlikely(actual != expect)) {
|
|
data_error(&q[l], expect, actual, use_for_badram);
|
|
}
|
|
write_word(&q[l], st->lane[l]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
void seed_lanes(vec_state_t *st, testword_t seed, bool splat)
|
|
{
|
|
if (splat) {
|
|
testword_t s = seed != 0 ? seed : lane_salt[0];
|
|
s = prsg(s);
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
st->lane[l] = s;
|
|
}
|
|
} else {
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
testword_t s = seed ^ lane_salt[l];
|
|
if (s == 0) {
|
|
s = lane_salt[l];
|
|
}
|
|
st->lane[l] = prsg(s);
|
|
}
|
|
}
|
|
}
|
|
|
|
void vec_fill(vec_state_t *st, testword_t *p, size_t nblocks, bool splat)
|
|
{
|
|
#if defined(__x86_64__)
|
|
if (simd_tier == SIMD_AVX2) {
|
|
vec_fill_avx2(st, p, nblocks, splat);
|
|
return;
|
|
}
|
|
if (simd_tier == SIMD_SSE2) {
|
|
vec_fill_sse2(st, p, nblocks, splat);
|
|
return;
|
|
}
|
|
#elif defined(__aarch64__)
|
|
if (simd_tier == SIMD_NEON) {
|
|
vec_fill_neon(st, p, nblocks, splat);
|
|
return;
|
|
}
|
|
#endif
|
|
vec_fill_scalar(st, p, nblocks, splat);
|
|
}
|
|
|
|
void vec_check_fwd(vec_state_t *st, testword_t *p, size_t nblocks, bool splat, bool use_for_badram)
|
|
{
|
|
#if defined(__x86_64__)
|
|
if (simd_tier != SIMD_NONE) {
|
|
while (nblocks > 0) {
|
|
size_t done = (simd_tier == SIMD_AVX2) ? vec_scan_fwd_avx2(st, p, nblocks, splat)
|
|
: vec_scan_fwd_sse2(st, p, nblocks, splat);
|
|
p += done * VEC_LANES;
|
|
nblocks -= done;
|
|
if (nblocks == 0) {
|
|
break;
|
|
}
|
|
// The scan stopped at a mismatching block. Report and fix it up,
|
|
// then resume the scan on the remaining blocks.
|
|
vec_check_fwd_scalar(st, p, 1, splat, use_for_badram);
|
|
p += VEC_LANES;
|
|
nblocks--;
|
|
}
|
|
return;
|
|
}
|
|
#elif defined(__aarch64__)
|
|
if (simd_tier == SIMD_NEON) {
|
|
while (nblocks > 0) {
|
|
size_t done = vec_scan_fwd_neon(st, p, nblocks, splat);
|
|
p += done * VEC_LANES;
|
|
nblocks -= done;
|
|
if (nblocks == 0) {
|
|
break;
|
|
}
|
|
// The scan stopped at a mismatching block. Report and fix it up,
|
|
// then resume the scan on the remaining blocks.
|
|
vec_check_fwd_scalar(st, p, 1, splat, use_for_badram);
|
|
p += VEC_LANES;
|
|
nblocks--;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
vec_check_fwd_scalar(st, p, nblocks, splat, use_for_badram);
|
|
}
|
|
|
|
void vec_check_rev(vec_state_t *st, testword_t *p, size_t nblocks, bool splat, bool use_for_badram)
|
|
{
|
|
#if defined(__x86_64__)
|
|
if (simd_tier != SIMD_NONE) {
|
|
testword_t *q = p + (nblocks - 1) * VEC_LANES;
|
|
while (nblocks > 0) {
|
|
size_t done = (simd_tier == SIMD_AVX2) ? vec_scan_rev_avx2(st, q, nblocks, splat)
|
|
: vec_scan_rev_sse2(st, q, nblocks, splat);
|
|
q -= done * VEC_LANES;
|
|
nblocks -= done;
|
|
if (nblocks == 0) {
|
|
break;
|
|
}
|
|
// The scan stopped at a mismatching block, with the lane states
|
|
// already stepped back to it. Report and fix it up, then resume.
|
|
// Unlike the forward case, the scalar kernel cannot be reused
|
|
// here as it would step the lane states back a second time.
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
testword_t expect = ~st->lane[l];
|
|
testword_t actual = read_word(&q[l]);
|
|
if (unlikely(actual != expect)) {
|
|
data_error(&q[l], expect, actual, use_for_badram);
|
|
}
|
|
write_word(&q[l], st->lane[l]);
|
|
}
|
|
q -= VEC_LANES;
|
|
nblocks--;
|
|
}
|
|
return;
|
|
}
|
|
#elif defined(__aarch64__)
|
|
if (simd_tier == SIMD_NEON) {
|
|
testword_t *q = p + (nblocks - 1) * VEC_LANES;
|
|
while (nblocks > 0) {
|
|
size_t done = vec_scan_rev_neon(st, q, nblocks, splat);
|
|
q -= done * VEC_LANES;
|
|
nblocks -= done;
|
|
if (nblocks == 0) {
|
|
break;
|
|
}
|
|
// The scan stopped at a mismatching block.
|
|
for (int l = 0; l < VEC_LANES; l++) {
|
|
testword_t expect = ~st->lane[l];
|
|
testword_t actual = read_word(&q[l]);
|
|
if (unlikely(actual != expect)) {
|
|
data_error(&q[l], expect, actual, use_for_badram);
|
|
}
|
|
write_word(&q[l], st->lane[l]);
|
|
}
|
|
q -= VEC_LANES;
|
|
nblocks--;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
vec_check_rev_scalar(st, p, nblocks, splat, use_for_badram);
|
|
}
|