xref: /openbmc/linux/mm/memtest.c (revision 4a20799d11f64e6b8725cacc7619b1ae1dbf9acd)
1*4a20799dSVladimir Murzin #include <linux/kernel.h>
2*4a20799dSVladimir Murzin #include <linux/errno.h>
3*4a20799dSVladimir Murzin #include <linux/string.h>
4*4a20799dSVladimir Murzin #include <linux/types.h>
5*4a20799dSVladimir Murzin #include <linux/mm.h>
6*4a20799dSVladimir Murzin #include <linux/smp.h>
7*4a20799dSVladimir Murzin #include <linux/init.h>
8*4a20799dSVladimir Murzin #include <linux/pfn.h>
9*4a20799dSVladimir Murzin #include <linux/memblock.h>
10*4a20799dSVladimir Murzin 
11*4a20799dSVladimir Murzin static u64 patterns[] __initdata = {
12*4a20799dSVladimir Murzin 	/* The first entry has to be 0 to leave memtest with zeroed memory */
13*4a20799dSVladimir Murzin 	0,
14*4a20799dSVladimir Murzin 	0xffffffffffffffffULL,
15*4a20799dSVladimir Murzin 	0x5555555555555555ULL,
16*4a20799dSVladimir Murzin 	0xaaaaaaaaaaaaaaaaULL,
17*4a20799dSVladimir Murzin 	0x1111111111111111ULL,
18*4a20799dSVladimir Murzin 	0x2222222222222222ULL,
19*4a20799dSVladimir Murzin 	0x4444444444444444ULL,
20*4a20799dSVladimir Murzin 	0x8888888888888888ULL,
21*4a20799dSVladimir Murzin 	0x3333333333333333ULL,
22*4a20799dSVladimir Murzin 	0x6666666666666666ULL,
23*4a20799dSVladimir Murzin 	0x9999999999999999ULL,
24*4a20799dSVladimir Murzin 	0xccccccccccccccccULL,
25*4a20799dSVladimir Murzin 	0x7777777777777777ULL,
26*4a20799dSVladimir Murzin 	0xbbbbbbbbbbbbbbbbULL,
27*4a20799dSVladimir Murzin 	0xddddddddddddddddULL,
28*4a20799dSVladimir Murzin 	0xeeeeeeeeeeeeeeeeULL,
29*4a20799dSVladimir Murzin 	0x7a6c7258554e494cULL, /* yeah ;-) */
30*4a20799dSVladimir Murzin };
31*4a20799dSVladimir Murzin 
32*4a20799dSVladimir Murzin static void __init reserve_bad_mem(u64 pattern, u64 start_bad, u64 end_bad)
33*4a20799dSVladimir Murzin {
34*4a20799dSVladimir Murzin 	printk(KERN_INFO "  %016llx bad mem addr %010llx - %010llx reserved\n",
35*4a20799dSVladimir Murzin 	       (unsigned long long) pattern,
36*4a20799dSVladimir Murzin 	       (unsigned long long) start_bad,
37*4a20799dSVladimir Murzin 	       (unsigned long long) end_bad);
38*4a20799dSVladimir Murzin 	memblock_reserve(start_bad, end_bad - start_bad);
39*4a20799dSVladimir Murzin }
40*4a20799dSVladimir Murzin 
41*4a20799dSVladimir Murzin static void __init memtest(u64 pattern, u64 start_phys, u64 size)
42*4a20799dSVladimir Murzin {
43*4a20799dSVladimir Murzin 	u64 *p, *start, *end;
44*4a20799dSVladimir Murzin 	u64 start_bad, last_bad;
45*4a20799dSVladimir Murzin 	u64 start_phys_aligned;
46*4a20799dSVladimir Murzin 	const size_t incr = sizeof(pattern);
47*4a20799dSVladimir Murzin 
48*4a20799dSVladimir Murzin 	start_phys_aligned = ALIGN(start_phys, incr);
49*4a20799dSVladimir Murzin 	start = __va(start_phys_aligned);
50*4a20799dSVladimir Murzin 	end = start + (size - (start_phys_aligned - start_phys)) / incr;
51*4a20799dSVladimir Murzin 	start_bad = 0;
52*4a20799dSVladimir Murzin 	last_bad = 0;
53*4a20799dSVladimir Murzin 
54*4a20799dSVladimir Murzin 	for (p = start; p < end; p++)
55*4a20799dSVladimir Murzin 		*p = pattern;
56*4a20799dSVladimir Murzin 
57*4a20799dSVladimir Murzin 	for (p = start; p < end; p++, start_phys_aligned += incr) {
58*4a20799dSVladimir Murzin 		if (*p == pattern)
59*4a20799dSVladimir Murzin 			continue;
60*4a20799dSVladimir Murzin 		if (start_phys_aligned == last_bad + incr) {
61*4a20799dSVladimir Murzin 			last_bad += incr;
62*4a20799dSVladimir Murzin 			continue;
63*4a20799dSVladimir Murzin 		}
64*4a20799dSVladimir Murzin 		if (start_bad)
65*4a20799dSVladimir Murzin 			reserve_bad_mem(pattern, start_bad, last_bad + incr);
66*4a20799dSVladimir Murzin 		start_bad = last_bad = start_phys_aligned;
67*4a20799dSVladimir Murzin 	}
68*4a20799dSVladimir Murzin 	if (start_bad)
69*4a20799dSVladimir Murzin 		reserve_bad_mem(pattern, start_bad, last_bad + incr);
70*4a20799dSVladimir Murzin }
71*4a20799dSVladimir Murzin 
72*4a20799dSVladimir Murzin static void __init do_one_pass(u64 pattern, u64 start, u64 end)
73*4a20799dSVladimir Murzin {
74*4a20799dSVladimir Murzin 	u64 i;
75*4a20799dSVladimir Murzin 	phys_addr_t this_start, this_end;
76*4a20799dSVladimir Murzin 
77*4a20799dSVladimir Murzin 	for_each_free_mem_range(i, NUMA_NO_NODE, &this_start, &this_end, NULL) {
78*4a20799dSVladimir Murzin 		this_start = clamp_t(phys_addr_t, this_start, start, end);
79*4a20799dSVladimir Murzin 		this_end = clamp_t(phys_addr_t, this_end, start, end);
80*4a20799dSVladimir Murzin 		if (this_start < this_end) {
81*4a20799dSVladimir Murzin 			printk(KERN_INFO "  %010llx - %010llx pattern %016llx\n",
82*4a20799dSVladimir Murzin 			       (unsigned long long)this_start,
83*4a20799dSVladimir Murzin 			       (unsigned long long)this_end,
84*4a20799dSVladimir Murzin 			       (unsigned long long)cpu_to_be64(pattern));
85*4a20799dSVladimir Murzin 			memtest(pattern, this_start, this_end - this_start);
86*4a20799dSVladimir Murzin 		}
87*4a20799dSVladimir Murzin 	}
88*4a20799dSVladimir Murzin }
89*4a20799dSVladimir Murzin 
90*4a20799dSVladimir Murzin /* default is disabled */
91*4a20799dSVladimir Murzin static int memtest_pattern __initdata;
92*4a20799dSVladimir Murzin 
93*4a20799dSVladimir Murzin static int __init parse_memtest(char *arg)
94*4a20799dSVladimir Murzin {
95*4a20799dSVladimir Murzin 	if (arg)
96*4a20799dSVladimir Murzin 		memtest_pattern = simple_strtoul(arg, NULL, 0);
97*4a20799dSVladimir Murzin 	else
98*4a20799dSVladimir Murzin 		memtest_pattern = ARRAY_SIZE(patterns);
99*4a20799dSVladimir Murzin 
100*4a20799dSVladimir Murzin 	return 0;
101*4a20799dSVladimir Murzin }
102*4a20799dSVladimir Murzin 
103*4a20799dSVladimir Murzin early_param("memtest", parse_memtest);
104*4a20799dSVladimir Murzin 
105*4a20799dSVladimir Murzin void __init early_memtest(unsigned long start, unsigned long end)
106*4a20799dSVladimir Murzin {
107*4a20799dSVladimir Murzin 	unsigned int i;
108*4a20799dSVladimir Murzin 	unsigned int idx = 0;
109*4a20799dSVladimir Murzin 
110*4a20799dSVladimir Murzin 	if (!memtest_pattern)
111*4a20799dSVladimir Murzin 		return;
112*4a20799dSVladimir Murzin 
113*4a20799dSVladimir Murzin 	printk(KERN_INFO "early_memtest: # of tests: %d\n", memtest_pattern);
114*4a20799dSVladimir Murzin 	for (i = memtest_pattern-1; i < UINT_MAX; --i) {
115*4a20799dSVladimir Murzin 		idx = i % ARRAY_SIZE(patterns);
116*4a20799dSVladimir Murzin 		do_one_pass(patterns[idx], start, end);
117*4a20799dSVladimir Murzin 	}
118*4a20799dSVladimir Murzin }
119