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