xref: /kernel/linux/linux-5.10/mm/memtest.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/kernel.h>
38c2ecf20Sopenharmony_ci#include <linux/types.h>
48c2ecf20Sopenharmony_ci#include <linux/init.h>
58c2ecf20Sopenharmony_ci#include <linux/memblock.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_cistatic u64 patterns[] __initdata = {
88c2ecf20Sopenharmony_ci	/* The first entry has to be 0 to leave memtest with zeroed memory */
98c2ecf20Sopenharmony_ci	0,
108c2ecf20Sopenharmony_ci	0xffffffffffffffffULL,
118c2ecf20Sopenharmony_ci	0x5555555555555555ULL,
128c2ecf20Sopenharmony_ci	0xaaaaaaaaaaaaaaaaULL,
138c2ecf20Sopenharmony_ci	0x1111111111111111ULL,
148c2ecf20Sopenharmony_ci	0x2222222222222222ULL,
158c2ecf20Sopenharmony_ci	0x4444444444444444ULL,
168c2ecf20Sopenharmony_ci	0x8888888888888888ULL,
178c2ecf20Sopenharmony_ci	0x3333333333333333ULL,
188c2ecf20Sopenharmony_ci	0x6666666666666666ULL,
198c2ecf20Sopenharmony_ci	0x9999999999999999ULL,
208c2ecf20Sopenharmony_ci	0xccccccccccccccccULL,
218c2ecf20Sopenharmony_ci	0x7777777777777777ULL,
228c2ecf20Sopenharmony_ci	0xbbbbbbbbbbbbbbbbULL,
238c2ecf20Sopenharmony_ci	0xddddddddddddddddULL,
248c2ecf20Sopenharmony_ci	0xeeeeeeeeeeeeeeeeULL,
258c2ecf20Sopenharmony_ci	0x7a6c7258554e494cULL, /* yeah ;-) */
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void __init reserve_bad_mem(u64 pattern, phys_addr_t start_bad, phys_addr_t end_bad)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	pr_info("  %016llx bad mem addr %pa - %pa reserved\n",
318c2ecf20Sopenharmony_ci		cpu_to_be64(pattern), &start_bad, &end_bad);
328c2ecf20Sopenharmony_ci	memblock_reserve(start_bad, end_bad - start_bad);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void __init memtest(u64 pattern, phys_addr_t start_phys, phys_addr_t size)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	u64 *p, *start, *end;
388c2ecf20Sopenharmony_ci	phys_addr_t start_bad, last_bad;
398c2ecf20Sopenharmony_ci	phys_addr_t start_phys_aligned;
408c2ecf20Sopenharmony_ci	const size_t incr = sizeof(pattern);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	start_phys_aligned = ALIGN(start_phys, incr);
438c2ecf20Sopenharmony_ci	start = __va(start_phys_aligned);
448c2ecf20Sopenharmony_ci	end = start + (size - (start_phys_aligned - start_phys)) / incr;
458c2ecf20Sopenharmony_ci	start_bad = 0;
468c2ecf20Sopenharmony_ci	last_bad = 0;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	for (p = start; p < end; p++)
498c2ecf20Sopenharmony_ci		*p = pattern;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	for (p = start; p < end; p++, start_phys_aligned += incr) {
528c2ecf20Sopenharmony_ci		if (*p == pattern)
538c2ecf20Sopenharmony_ci			continue;
548c2ecf20Sopenharmony_ci		if (start_phys_aligned == last_bad + incr) {
558c2ecf20Sopenharmony_ci			last_bad += incr;
568c2ecf20Sopenharmony_ci			continue;
578c2ecf20Sopenharmony_ci		}
588c2ecf20Sopenharmony_ci		if (start_bad)
598c2ecf20Sopenharmony_ci			reserve_bad_mem(pattern, start_bad, last_bad + incr);
608c2ecf20Sopenharmony_ci		start_bad = last_bad = start_phys_aligned;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci	if (start_bad)
638c2ecf20Sopenharmony_ci		reserve_bad_mem(pattern, start_bad, last_bad + incr);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void __init do_one_pass(u64 pattern, phys_addr_t start, phys_addr_t end)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	u64 i;
698c2ecf20Sopenharmony_ci	phys_addr_t this_start, this_end;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &this_start,
728c2ecf20Sopenharmony_ci				&this_end, NULL) {
738c2ecf20Sopenharmony_ci		this_start = clamp(this_start, start, end);
748c2ecf20Sopenharmony_ci		this_end = clamp(this_end, start, end);
758c2ecf20Sopenharmony_ci		if (this_start < this_end) {
768c2ecf20Sopenharmony_ci			pr_info("  %pa - %pa pattern %016llx\n",
778c2ecf20Sopenharmony_ci				&this_start, &this_end, cpu_to_be64(pattern));
788c2ecf20Sopenharmony_ci			memtest(pattern, this_start, this_end - this_start);
798c2ecf20Sopenharmony_ci		}
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* default is disabled */
848c2ecf20Sopenharmony_cistatic unsigned int memtest_pattern __initdata;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic int __init parse_memtest(char *arg)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int ret = 0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (arg)
918c2ecf20Sopenharmony_ci		ret = kstrtouint(arg, 0, &memtest_pattern);
928c2ecf20Sopenharmony_ci	else
938c2ecf20Sopenharmony_ci		memtest_pattern = ARRAY_SIZE(patterns);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	return ret;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ciearly_param("memtest", parse_memtest);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_civoid __init early_memtest(phys_addr_t start, phys_addr_t end)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	unsigned int i;
1038c2ecf20Sopenharmony_ci	unsigned int idx = 0;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (!memtest_pattern)
1068c2ecf20Sopenharmony_ci		return;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	pr_info("early_memtest: # of tests: %u\n", memtest_pattern);
1098c2ecf20Sopenharmony_ci	for (i = memtest_pattern-1; i < UINT_MAX; --i) {
1108c2ecf20Sopenharmony_ci		idx = i % ARRAY_SIZE(patterns);
1118c2ecf20Sopenharmony_ci		do_one_pass(patterns[idx], start, end);
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci}
114