18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/kernel.h> 38c2ecf20Sopenharmony_ci#include <linux/types.h> 48c2ecf20Sopenharmony_ci#include <linux/init.h> 58c2ecf20Sopenharmony_ci#include <linux/memblock.h> 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_cistatic u64 patterns[] __initdata = { 88c2ecf20Sopenharmony_ci /* The first entry has to be 0 to leave memtest with zeroed memory */ 98c2ecf20Sopenharmony_ci 0, 108c2ecf20Sopenharmony_ci 0xffffffffffffffffULL, 118c2ecf20Sopenharmony_ci 0x5555555555555555ULL, 128c2ecf20Sopenharmony_ci 0xaaaaaaaaaaaaaaaaULL, 138c2ecf20Sopenharmony_ci 0x1111111111111111ULL, 148c2ecf20Sopenharmony_ci 0x2222222222222222ULL, 158c2ecf20Sopenharmony_ci 0x4444444444444444ULL, 168c2ecf20Sopenharmony_ci 0x8888888888888888ULL, 178c2ecf20Sopenharmony_ci 0x3333333333333333ULL, 188c2ecf20Sopenharmony_ci 0x6666666666666666ULL, 198c2ecf20Sopenharmony_ci 0x9999999999999999ULL, 208c2ecf20Sopenharmony_ci 0xccccccccccccccccULL, 218c2ecf20Sopenharmony_ci 0x7777777777777777ULL, 228c2ecf20Sopenharmony_ci 0xbbbbbbbbbbbbbbbbULL, 238c2ecf20Sopenharmony_ci 0xddddddddddddddddULL, 248c2ecf20Sopenharmony_ci 0xeeeeeeeeeeeeeeeeULL, 258c2ecf20Sopenharmony_ci 0x7a6c7258554e494cULL, /* yeah ;-) */ 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void __init reserve_bad_mem(u64 pattern, phys_addr_t start_bad, phys_addr_t end_bad) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci pr_info(" %016llx bad mem addr %pa - %pa reserved\n", 318c2ecf20Sopenharmony_ci cpu_to_be64(pattern), &start_bad, &end_bad); 328c2ecf20Sopenharmony_ci memblock_reserve(start_bad, end_bad - start_bad); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void __init memtest(u64 pattern, phys_addr_t start_phys, phys_addr_t size) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci u64 *p, *start, *end; 388c2ecf20Sopenharmony_ci phys_addr_t start_bad, last_bad; 398c2ecf20Sopenharmony_ci phys_addr_t start_phys_aligned; 408c2ecf20Sopenharmony_ci const size_t incr = sizeof(pattern); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci start_phys_aligned = ALIGN(start_phys, incr); 438c2ecf20Sopenharmony_ci start = __va(start_phys_aligned); 448c2ecf20Sopenharmony_ci end = start + (size - (start_phys_aligned - start_phys)) / incr; 458c2ecf20Sopenharmony_ci start_bad = 0; 468c2ecf20Sopenharmony_ci last_bad = 0; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci for (p = start; p < end; p++) 498c2ecf20Sopenharmony_ci *p = pattern; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci for (p = start; p < end; p++, start_phys_aligned += incr) { 528c2ecf20Sopenharmony_ci if (*p == pattern) 538c2ecf20Sopenharmony_ci continue; 548c2ecf20Sopenharmony_ci if (start_phys_aligned == last_bad + incr) { 558c2ecf20Sopenharmony_ci last_bad += incr; 568c2ecf20Sopenharmony_ci continue; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci if (start_bad) 598c2ecf20Sopenharmony_ci reserve_bad_mem(pattern, start_bad, last_bad + incr); 608c2ecf20Sopenharmony_ci start_bad = last_bad = start_phys_aligned; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci if (start_bad) 638c2ecf20Sopenharmony_ci reserve_bad_mem(pattern, start_bad, last_bad + incr); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void __init do_one_pass(u64 pattern, phys_addr_t start, phys_addr_t end) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci u64 i; 698c2ecf20Sopenharmony_ci phys_addr_t this_start, this_end; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &this_start, 728c2ecf20Sopenharmony_ci &this_end, NULL) { 738c2ecf20Sopenharmony_ci this_start = clamp(this_start, start, end); 748c2ecf20Sopenharmony_ci this_end = clamp(this_end, start, end); 758c2ecf20Sopenharmony_ci if (this_start < this_end) { 768c2ecf20Sopenharmony_ci pr_info(" %pa - %pa pattern %016llx\n", 778c2ecf20Sopenharmony_ci &this_start, &this_end, cpu_to_be64(pattern)); 788c2ecf20Sopenharmony_ci memtest(pattern, this_start, this_end - this_start); 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* default is disabled */ 848c2ecf20Sopenharmony_cistatic unsigned int memtest_pattern __initdata; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int __init parse_memtest(char *arg) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int ret = 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (arg) 918c2ecf20Sopenharmony_ci ret = kstrtouint(arg, 0, &memtest_pattern); 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci memtest_pattern = ARRAY_SIZE(patterns); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ciearly_param("memtest", parse_memtest); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_civoid __init early_memtest(phys_addr_t start, phys_addr_t end) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci unsigned int i; 1038c2ecf20Sopenharmony_ci unsigned int idx = 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (!memtest_pattern) 1068c2ecf20Sopenharmony_ci return; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci pr_info("early_memtest: # of tests: %u\n", memtest_pattern); 1098c2ecf20Sopenharmony_ci for (i = memtest_pattern-1; i < UINT_MAX; --i) { 1108c2ecf20Sopenharmony_ci idx = i % ARRAY_SIZE(patterns); 1118c2ecf20Sopenharmony_ci do_one_pass(patterns[idx], start, end); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci} 114