Files
memtest86plus/tests/vec_prsg.c
T
Sam Demeulemeester 7fc312d5b0 Add support for aarch64 (ARM64) architecture (#626)
* 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
2026-07-12 13:27:45 +02:00

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);
}