aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/mm/memtest.c
blob: 18d244f702059b59f88b94f8da6a7312f43d58cc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/pfn.h>

#include <asm/e820.h>

static u64 patterns[] __initdata = {
	0,
	0xffffffffffffffffULL,
	0x5555555555555555ULL,
	0xaaaaaaaaaaaaaaaaULL,
	0x1111111111111111ULL,
	0x2222222222222222ULL,
	0x4444444444444444ULL,
	0x8888888888888888ULL,
	0x3333333333333333ULL,
	0x6666666666666666ULL,
	0x9999999999999999ULL,
	0xccccccccccccccccULL,
	0x7777777777777777ULL,
	0xbbbbbbbbbbbbbbbbULL,
	0xddddddddddddddddULL,
	0xeeeeeeeeeeeeeeeeULL,
	0x7a6c7258554e494cULL, /* yeah ;-) */
};

static void __init reserve_bad_mem(u64 pattern, u64 start_bad, u64 end_bad)
{
	printk(KERN_INFO "  %016llx bad mem addr %010llx - %010llx reserved\n",
	       (unsigned long long) pattern,
	       (unsigned long long) start_bad,
	       (unsigned long long) end_bad);
	reserve_early(start_bad, end_bad, "BAD RAM");
}

static void __init memtest(u64 pattern, u64 start_phys, u64 size)
{
	u64 *p, *start, *end;
	u64 start_bad, last_bad;
	u64 start_phys_aligned;
	const size_t incr = sizeof(pattern);

	start_phys_aligned = ALIGN(start_phys, incr);
	start = __va(start_phys_aligned);
	end = start + (size - (start_phys_aligned - start_phys)) / incr;
	start_bad = 0;
	last_bad = 0;

	for (p = start; p < end; p++)
		*p = pattern;

	for (p = start; p < end; p++, start_phys_aligned += incr) {
		if (*p == pattern)
			continue;
		if (start_phys_aligned == last_bad + incr) {
			last_bad += incr;
			continue;
		}
		if (start_bad)
			reserve_bad_mem(pattern, start_bad, last_bad + incr);
		start_bad = last_bad = start_phys_aligned;
	}
	if (start_bad)
		reserve_bad_mem(pattern, start_bad, last_bad + incr);
}

static void __init do_one_pass(u64 pattern, u64 start, u64 end)
{
	u64 size = 0;

	while (start < end) {
		start = find_e820_area_size(start, &size, 1);

		/* done ? */
		if (start >= end)
			break;
		if (start + size > end)
			size = end - start;

		printk(KERN_INFO "  %010llx - %010llx pattern %016llx\n",
		       (unsigned long long) start,
		       (unsigned long long) start + size,
		       (unsigned long long) cpu_to_be64(pattern));
		memtest(pattern, start, size);

		start += size;
	}
}

/* default is disabled */
static int memtest_pattern __initdata;

static int __init parse_memtest(char *arg)
{
	if (arg)
		memtest_pattern = simple_strtoul(arg, NULL, 0);
	else
		memtest_pattern = ARRAY_SIZE(patterns);

	return 0;
}

early_param("memtest", parse_memtest);

void __init early_memtest(unsigned long start, unsigned long end)
{
	unsigned int i;
	unsigned int idx = 0;

	if (!memtest_pattern)
		return;

	printk(KERN_INFO "early_memtest: # of tests: %d\n", memtest_pattern);
	for (i = 0; i < memtest_pattern; i++) {
		idx = i % ARRAY_SIZE(patterns);
		do_one_pass(patterns[idx], start, end);
	}

	if (idx > 0) {
		printk(KERN_INFO "early_memtest: wipe out "
		       "test pattern from memory\n");
		/* additional test with pattern 0 will do this */
		do_one_pass(0, start, end);
	}
}