xref: /openbmc/linux/mm/memtest.c (revision 46eeaa11bdd1bc9e077bdf741d32ca7235d263c6)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
24a20799dSVladimir Murzin #include <linux/kernel.h>
34a20799dSVladimir Murzin #include <linux/types.h>
44a20799dSVladimir Murzin #include <linux/init.h>
54a20799dSVladimir Murzin #include <linux/memblock.h>
63f32c49eSKefeng Wang #include <linux/seq_file.h>
74a20799dSVladimir Murzin 
83f32c49eSKefeng Wang static bool early_memtest_done;
93f32c49eSKefeng Wang static phys_addr_t early_memtest_bad_size;
10bd23024bSTomas Mudrunka 
114a20799dSVladimir Murzin static u64 patterns[] __initdata = {
124a20799dSVladimir Murzin 	/* The first entry has to be 0 to leave memtest with zeroed memory */
134a20799dSVladimir Murzin 	0,
144a20799dSVladimir Murzin 	0xffffffffffffffffULL,
154a20799dSVladimir Murzin 	0x5555555555555555ULL,
164a20799dSVladimir Murzin 	0xaaaaaaaaaaaaaaaaULL,
174a20799dSVladimir Murzin 	0x1111111111111111ULL,
184a20799dSVladimir Murzin 	0x2222222222222222ULL,
194a20799dSVladimir Murzin 	0x4444444444444444ULL,
204a20799dSVladimir Murzin 	0x8888888888888888ULL,
214a20799dSVladimir Murzin 	0x3333333333333333ULL,
224a20799dSVladimir Murzin 	0x6666666666666666ULL,
234a20799dSVladimir Murzin 	0x9999999999999999ULL,
244a20799dSVladimir Murzin 	0xccccccccccccccccULL,
254a20799dSVladimir Murzin 	0x7777777777777777ULL,
264a20799dSVladimir Murzin 	0xbbbbbbbbbbbbbbbbULL,
274a20799dSVladimir Murzin 	0xddddddddddddddddULL,
284a20799dSVladimir Murzin 	0xeeeeeeeeeeeeeeeeULL,
294a20799dSVladimir Murzin 	0x7a6c7258554e494cULL, /* yeah ;-) */
304a20799dSVladimir Murzin };
314a20799dSVladimir Murzin 
reserve_bad_mem(u64 pattern,phys_addr_t start_bad,phys_addr_t end_bad)327f70baeeSVladimir Murzin static void __init reserve_bad_mem(u64 pattern, phys_addr_t start_bad, phys_addr_t end_bad)
334a20799dSVladimir Murzin {
34f373bafcSVladimir Murzin 	pr_info("  %016llx bad mem addr %pa - %pa reserved\n",
35f373bafcSVladimir Murzin 		cpu_to_be64(pattern), &start_bad, &end_bad);
364a20799dSVladimir Murzin 	memblock_reserve(start_bad, end_bad - start_bad);
37bd23024bSTomas Mudrunka 	early_memtest_bad_size += (end_bad - start_bad);
384a20799dSVladimir Murzin }
394a20799dSVladimir Murzin 
memtest(u64 pattern,phys_addr_t start_phys,phys_addr_t size)407f70baeeSVladimir Murzin static void __init memtest(u64 pattern, phys_addr_t start_phys, phys_addr_t size)
414a20799dSVladimir Murzin {
424a20799dSVladimir Murzin 	u64 *p, *start, *end;
437f70baeeSVladimir Murzin 	phys_addr_t start_bad, last_bad;
447f70baeeSVladimir Murzin 	phys_addr_t start_phys_aligned;
454a20799dSVladimir Murzin 	const size_t incr = sizeof(pattern);
464a20799dSVladimir Murzin 
474a20799dSVladimir Murzin 	start_phys_aligned = ALIGN(start_phys, incr);
484a20799dSVladimir Murzin 	start = __va(start_phys_aligned);
494a20799dSVladimir Murzin 	end = start + (size - (start_phys_aligned - start_phys)) / incr;
504a20799dSVladimir Murzin 	start_bad = 0;
514a20799dSVladimir Murzin 	last_bad = 0;
524a20799dSVladimir Murzin 
534a20799dSVladimir Murzin 	for (p = start; p < end; p++)
54*f1d93b2aSQiang Zhang 		WRITE_ONCE(*p, pattern);
554a20799dSVladimir Murzin 
564a20799dSVladimir Murzin 	for (p = start; p < end; p++, start_phys_aligned += incr) {
57*f1d93b2aSQiang Zhang 		if (READ_ONCE(*p) == pattern)
584a20799dSVladimir Murzin 			continue;
594a20799dSVladimir Murzin 		if (start_phys_aligned == last_bad + incr) {
604a20799dSVladimir Murzin 			last_bad += incr;
614a20799dSVladimir Murzin 			continue;
624a20799dSVladimir Murzin 		}
634a20799dSVladimir Murzin 		if (start_bad)
644a20799dSVladimir Murzin 			reserve_bad_mem(pattern, start_bad, last_bad + incr);
654a20799dSVladimir Murzin 		start_bad = last_bad = start_phys_aligned;
664a20799dSVladimir Murzin 	}
674a20799dSVladimir Murzin 	if (start_bad)
684a20799dSVladimir Murzin 		reserve_bad_mem(pattern, start_bad, last_bad + incr);
69bd23024bSTomas Mudrunka 
70bd23024bSTomas Mudrunka 	early_memtest_done = true;
714a20799dSVladimir Murzin }
724a20799dSVladimir Murzin 
do_one_pass(u64 pattern,phys_addr_t start,phys_addr_t end)737f70baeeSVladimir Murzin static void __init do_one_pass(u64 pattern, phys_addr_t start, phys_addr_t end)
744a20799dSVladimir Murzin {
754a20799dSVladimir Murzin 	u64 i;
764a20799dSVladimir Murzin 	phys_addr_t this_start, this_end;
774a20799dSVladimir Murzin 
78fc6daaf9STony Luck 	for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &this_start,
79fc6daaf9STony Luck 				&this_end, NULL) {
807f70baeeSVladimir Murzin 		this_start = clamp(this_start, start, end);
817f70baeeSVladimir Murzin 		this_end = clamp(this_end, start, end);
824a20799dSVladimir Murzin 		if (this_start < this_end) {
83f373bafcSVladimir Murzin 			pr_info("  %pa - %pa pattern %016llx\n",
84f373bafcSVladimir Murzin 				&this_start, &this_end, cpu_to_be64(pattern));
854a20799dSVladimir Murzin 			memtest(pattern, this_start, this_end - this_start);
864a20799dSVladimir Murzin 		}
874a20799dSVladimir Murzin 	}
884a20799dSVladimir Murzin }
894a20799dSVladimir Murzin 
904a20799dSVladimir Murzin /* default is disabled */
9106f80596SVladimir Murzin static unsigned int memtest_pattern __initdata;
924a20799dSVladimir Murzin 
parse_memtest(char * arg)934a20799dSVladimir Murzin static int __init parse_memtest(char *arg)
944a20799dSVladimir Murzin {
9506f80596SVladimir Murzin 	int ret = 0;
9606f80596SVladimir Murzin 
974a20799dSVladimir Murzin 	if (arg)
9806f80596SVladimir Murzin 		ret = kstrtouint(arg, 0, &memtest_pattern);
994a20799dSVladimir Murzin 	else
1004a20799dSVladimir Murzin 		memtest_pattern = ARRAY_SIZE(patterns);
1014a20799dSVladimir Murzin 
10206f80596SVladimir Murzin 	return ret;
1034a20799dSVladimir Murzin }
1044a20799dSVladimir Murzin 
1054a20799dSVladimir Murzin early_param("memtest", parse_memtest);
1064a20799dSVladimir Murzin 
early_memtest(phys_addr_t start,phys_addr_t end)1077f70baeeSVladimir Murzin void __init early_memtest(phys_addr_t start, phys_addr_t end)
1084a20799dSVladimir Murzin {
1094a20799dSVladimir Murzin 	unsigned int i;
1104a20799dSVladimir Murzin 	unsigned int idx = 0;
1114a20799dSVladimir Murzin 
1124a20799dSVladimir Murzin 	if (!memtest_pattern)
1134a20799dSVladimir Murzin 		return;
1144a20799dSVladimir Murzin 
115f373bafcSVladimir Murzin 	pr_info("early_memtest: # of tests: %u\n", memtest_pattern);
1164a20799dSVladimir Murzin 	for (i = memtest_pattern-1; i < UINT_MAX; --i) {
1174a20799dSVladimir Murzin 		idx = i % ARRAY_SIZE(patterns);
1184a20799dSVladimir Murzin 		do_one_pass(patterns[idx], start, end);
1194a20799dSVladimir Murzin 	}
1204a20799dSVladimir Murzin }
1213f32c49eSKefeng Wang 
memtest_report_meminfo(struct seq_file * m)1223f32c49eSKefeng Wang void memtest_report_meminfo(struct seq_file *m)
1233f32c49eSKefeng Wang {
1243f32c49eSKefeng Wang 	unsigned long early_memtest_bad_size_kb;
1253f32c49eSKefeng Wang 
1263f32c49eSKefeng Wang 	if (!IS_ENABLED(CONFIG_PROC_FS))
1273f32c49eSKefeng Wang 		return;
1283f32c49eSKefeng Wang 
1293f32c49eSKefeng Wang 	if (!early_memtest_done)
1303f32c49eSKefeng Wang 		return;
1313f32c49eSKefeng Wang 
1323f32c49eSKefeng Wang 	early_memtest_bad_size_kb = early_memtest_bad_size >> 10;
1333f32c49eSKefeng Wang 	if (early_memtest_bad_size && !early_memtest_bad_size_kb)
1343f32c49eSKefeng Wang 		early_memtest_bad_size_kb = 1;
1353f32c49eSKefeng Wang 	/* When 0 is reported, it means there actually was a successful test */
1363f32c49eSKefeng Wang 	seq_printf(m, "EarlyMemtestBad:   %5lu kB\n", early_memtest_bad_size_kb);
1373f32c49eSKefeng Wang }
138