11f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu#include <linux/kernel.h>
21f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu#include <linux/errno.h>
31f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu#include <linux/string.h>
41f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu#include <linux/types.h>
51f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu#include <linux/mm.h>
61f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu#include <linux/smp.h>
71f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu#include <linux/init.h>
81f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu#include <linux/pfn.h>
9a9ce6bc15100023b411f8117e53a016d61889800Yinghai Lu#include <linux/memblock.h>
101f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu
116d74171bf7315257d276aa35400c5a8d6a993f19Andreas Herrmannstatic u64 patterns[] __initdata = {
126d74171bf7315257d276aa35400c5a8d6a993f19Andreas Herrmann	0,
136d74171bf7315257d276aa35400c5a8d6a993f19Andreas Herrmann	0xffffffffffffffffULL,
146d74171bf7315257d276aa35400c5a8d6a993f19Andreas Herrmann	0x5555555555555555ULL,
156d74171bf7315257d276aa35400c5a8d6a993f19Andreas Herrmann	0xaaaaaaaaaaaaaaaaULL,
1663823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0x1111111111111111ULL,
1763823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0x2222222222222222ULL,
1863823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0x4444444444444444ULL,
1963823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0x8888888888888888ULL,
2063823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0x3333333333333333ULL,
2163823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0x6666666666666666ULL,
2263823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0x9999999999999999ULL,
2363823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0xccccccccccccccccULL,
2463823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0x7777777777777777ULL,
2563823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0xbbbbbbbbbbbbbbbbULL,
2663823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0xddddddddddddddddULL,
2763823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0xeeeeeeeeeeeeeeeeULL,
2863823126c221dd721ce7351b596b3b73aa943613Andreas Herrmann	0x7a6c7258554e494cULL, /* yeah ;-) */
296d74171bf7315257d276aa35400c5a8d6a993f19Andreas Herrmann};
3040823f737e5bd186a1156fb1c28f360260e1e084Andreas Herrmann
31570c9e69aaa84689fb8ed2a3a4af39ca54ba7a47Andreas Herrmannstatic void __init reserve_bad_mem(u64 pattern, u64 start_bad, u64 end_bad)
327dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann{
33570c9e69aaa84689fb8ed2a3a4af39ca54ba7a47Andreas Herrmann	printk(KERN_INFO "  %016llx bad mem addr %010llx - %010llx reserved\n",
34570c9e69aaa84689fb8ed2a3a4af39ca54ba7a47Andreas Herrmann	       (unsigned long long) pattern,
35570c9e69aaa84689fb8ed2a3a4af39ca54ba7a47Andreas Herrmann	       (unsigned long long) start_bad,
36570c9e69aaa84689fb8ed2a3a4af39ca54ba7a47Andreas Herrmann	       (unsigned long long) end_bad);
3724aa07882b672fff2da2f5c955759f0bd13d32d5Tejun Heo	memblock_reserve(start_bad, end_bad - start_bad);
387dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann}
397dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann
40570c9e69aaa84689fb8ed2a3a4af39ca54ba7a47Andreas Herrmannstatic void __init memtest(u64 pattern, u64 start_phys, u64 size)
411f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu{
429866b7e86a2ce4daa677be750e3ccbfc65d187f5Thomas Gleixner	u64 *p, *start, *end;
43570c9e69aaa84689fb8ed2a3a4af39ca54ba7a47Andreas Herrmann	u64 start_bad, last_bad;
44570c9e69aaa84689fb8ed2a3a4af39ca54ba7a47Andreas Herrmann	u64 start_phys_aligned;
459866b7e86a2ce4daa677be750e3ccbfc65d187f5Thomas Gleixner	const size_t incr = sizeof(pattern);
461f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu
471f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu	start_phys_aligned = ALIGN(start_phys, incr);
481f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu	start = __va(start_phys_aligned);
499866b7e86a2ce4daa677be750e3ccbfc65d187f5Thomas Gleixner	end = start + (size - (start_phys_aligned - start_phys)) / incr;
501f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu	start_bad = 0;
511f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu	last_bad = 0;
521f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu
53c9690998ef48ffefeccb91c70a7739eebdea57f9Andreas Herrmann	for (p = start; p < end; p++)
54c9690998ef48ffefeccb91c70a7739eebdea57f9Andreas Herrmann		*p = pattern;
559866b7e86a2ce4daa677be750e3ccbfc65d187f5Thomas Gleixner
56c9690998ef48ffefeccb91c70a7739eebdea57f9Andreas Herrmann	for (p = start; p < end; p++, start_phys_aligned += incr) {
57c9690998ef48ffefeccb91c70a7739eebdea57f9Andreas Herrmann		if (*p == pattern)
587dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann			continue;
597dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann		if (start_phys_aligned == last_bad + incr) {
607dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann			last_bad += incr;
617dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann			continue;
621f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu		}
637dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann		if (start_bad)
647dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann			reserve_bad_mem(pattern, start_bad, last_bad + incr);
657dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann		start_bad = last_bad = start_phys_aligned;
661f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu	}
677dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann	if (start_bad)
687dad169e57eda1f0aa6dc5ac43a898b4b0ced2c7Andreas Herrmann		reserve_bad_mem(pattern, start_bad, last_bad + incr);
691f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu}
701f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu
71bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmannstatic void __init do_one_pass(u64 pattern, u64 start, u64 end)
72bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann{
738d89ac808417e92a33fb5fa3c86352016643775aTejun Heo	u64 i;
748d89ac808417e92a33fb5fa3c86352016643775aTejun Heo	phys_addr_t this_start, this_end;
758d89ac808417e92a33fb5fa3c86352016643775aTejun Heo
768d89ac808417e92a33fb5fa3c86352016643775aTejun Heo	for_each_free_mem_range(i, MAX_NUMNODES, &this_start, &this_end, NULL) {
778d89ac808417e92a33fb5fa3c86352016643775aTejun Heo		this_start = clamp_t(phys_addr_t, this_start, start, end);
788d89ac808417e92a33fb5fa3c86352016643775aTejun Heo		this_end = clamp_t(phys_addr_t, this_end, start, end);
798d89ac808417e92a33fb5fa3c86352016643775aTejun Heo		if (this_start < this_end) {
808d89ac808417e92a33fb5fa3c86352016643775aTejun Heo			printk(KERN_INFO "  %010llx - %010llx pattern %016llx\n",
818d89ac808417e92a33fb5fa3c86352016643775aTejun Heo			       (unsigned long long)this_start,
828d89ac808417e92a33fb5fa3c86352016643775aTejun Heo			       (unsigned long long)this_end,
838d89ac808417e92a33fb5fa3c86352016643775aTejun Heo			       (unsigned long long)cpu_to_be64(pattern));
848d89ac808417e92a33fb5fa3c86352016643775aTejun Heo			memtest(pattern, this_start, this_end - this_start);
858d89ac808417e92a33fb5fa3c86352016643775aTejun Heo		}
86bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann	}
87bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann}
88bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann
891f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu/* default is disabled */
901f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lustatic int memtest_pattern __initdata;
911f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu
921f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lustatic int __init parse_memtest(char *arg)
931f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu{
941f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu	if (arg)
951f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu		memtest_pattern = simple_strtoul(arg, NULL, 0);
96d1a8e7792047f7dca7eb5759250e2c12800bf262Yinghai Lu	else
97d1a8e7792047f7dca7eb5759250e2c12800bf262Yinghai Lu		memtest_pattern = ARRAY_SIZE(patterns);
98d1a8e7792047f7dca7eb5759250e2c12800bf262Yinghai Lu
991f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu	return 0;
1001f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu}
1011f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu
1021f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Luearly_param("memtest", parse_memtest);
1031f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu
1041f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Luvoid __init early_memtest(unsigned long start, unsigned long end)
1051f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu{
1066d74171bf7315257d276aa35400c5a8d6a993f19Andreas Herrmann	unsigned int i;
107bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann	unsigned int idx = 0;
1081f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu
1091f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu	if (!memtest_pattern)
1101f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu		return;
1111f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu
112570c9e69aaa84689fb8ed2a3a4af39ca54ba7a47Andreas Herrmann	printk(KERN_INFO "early_memtest: # of tests: %d\n", memtest_pattern);
1136d74171bf7315257d276aa35400c5a8d6a993f19Andreas Herrmann	for (i = 0; i < memtest_pattern; i++) {
114bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann		idx = i % ARRAY_SIZE(patterns);
115bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann		do_one_pass(patterns[idx], start, end);
116bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann	}
117bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann
118bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann	if (idx > 0) {
119bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann		printk(KERN_INFO "early_memtest: wipe out "
120bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann		       "test pattern from memory\n");
121bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann		/* additional test with pattern 0 will do this */
122bfb4dc0da45f8fddc76eba7e62919420c7d6dae2Andreas Herrmann		do_one_pass(0, start, end);
1231f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu	}
1241f067167a83d1c7f80437fd1d32b55508aaca009Yinghai Lu}
125