Hello,

I've recently discovered the lack of the command line parameter memtest for ARM. So I've made a patch.

But I have some questions:

1. arch/x86/mm/memtest.c looks platform independ.
The only thing why I don't use it for arm, is because it uses 64bit pointers. Maybe it could be moved to mm/memtest.c. If so, the memtest32.c I'm using (basically a copy of memtest.c) could be moved there too.

2. Because the below memtest32.c is basically a copy of arch/x86/mm/memtest.c, I'm not sure if the mapping from physical to virtual locations there does fit (always) for ARM too. I know almost as much about the in-kernel memory organization on x86 as on ARM, which is not really that much (some theory about TLBs, some source code explorations, ..., but I'm working on it). ;)

3. I've just implemented a test for all the memory which is marked as free, leaving all reserved memory untested. But even if a full memory test could only be done in the boot-loader, I think at least some of the memory the kernel reserves for itself (e.g. for modules) could be tested too. I just haven't searched how/where this could be done. Maybe someone else has a hint or even a patch for the below patch.

4. I don't have an ARM box with bad memory. So my tests are a bit limited. Maybe someone else could do a test with real bad memory.

Anyway, I would still prefer to have at least the possibility to test some of the memory using the kernel instead of none at all. So if nobody offers a better solution, I would be glad if the below patch would find some friends. ;)

Regards,

Alexander


Here is how dmesg does look like (memtest=4):

--------- no error ---------
[    0.000000] Inode-cache hash table entries: 8192 (order: 3, 32768 bytes)
[    0.000000] early_memtest: # of tests: 4
[    0.000000]   0000000000 - 0000004000 pattern 00000000
[    0.000000]   0000000000 - 0000004000 pattern ffffffff
[    0.000000]   0000000000 - 0000004000 pattern 55555555
[    0.000000]   0000000000 - 0000004000 pattern aaaaaaaa
[    0.000000] early_memtest: wipe out test pattern from memory
[    0.000000]   0000000000 - 0000004000 pattern 00000000
[    0.000000] early_memtest: # of tests: 4
[    0.000000]   000054c000 - 0007ffb000 pattern 00000000
[    0.000000]   000054c000 - 0007ffb000 pattern ffffffff
[    0.000000]   000054c000 - 0007ffb000 pattern 55555555
[    0.000000]   000054c000 - 0007ffb000 pattern aaaaaaaa
[    0.000000] early_memtest: wipe out test pattern from memory
[    0.000000]   000054c000 - 0007ffb000 pattern 00000000
[    0.000000] Memory: 128MB = 128MB total
[    0.000000] Memory: 125648k/125648k available, 5424k reserved, 0K highmem
--------- no error ---------

--------- error inected (by sw) ---------
[    0.000000] Inode-cache hash table entries: 8192 (order: 3, 32768 bytes)
[    0.000000] early_memtest: # of tests: 4
[    0.000000]   0000000000 - 0000004000 pattern 00000000
[    0.000000]   0000000000 - 0000004000 pattern ffffffff
[    0.000000]   0000000000 - 0000004000 pattern 55555555
[    0.000000]   0000000000 - 0000004000 pattern aaaaaaaa
[    0.000000] early_memtest: wipe out test pattern from memory
[    0.000000]   0000000000 - 0000004000 pattern 00000000
[    0.000000] early_memtest: # of tests: 4
[    0.000000]   000054c000 - 0007ffb000 pattern 00000000
[    0.000000]   00000000 bad mem addr 0000600000 - 0000600014 reserved
[    0.000000]   0000600014 - 0007ffb000 pattern 00000000
[    0.000000]   000054c000 - 0000600000 pattern ffffffff
[    0.000000]   0000600014 - 0007ffb000 pattern ffffffff
[    0.000000]   000054c000 - 0000600000 pattern 55555555
[    0.000000]   0000600014 - 0007ffb000 pattern 55555555
[    0.000000]   000054c000 - 0000600000 pattern aaaaaaaa
[    0.000000]   0000600014 - 0007ffb000 pattern aaaaaaaa
[    0.000000] early_memtest: wipe out test pattern from memory
[    0.000000]   000054c000 - 0000600000 pattern 00000000
[    0.000000]   0000600014 - 0007ffb000 pattern 00000000
[    0.000000] Memory: 128MB = 128MB total
[    0.000000] Memory: 125648k/125648k available, 5424k reserved, 0K highmem
--------- error inected (by sw) ---------

--------- with hole (mem=99M@0x0 mem=28M@0x6400000) ---------
[    0.000000] Inode-cache hash table entries: 8192 (order: 3, 32768 bytes)
[    0.000000] early_memtest: # of tests: 4
[    0.000000]   0000000000 - 0000004000 pattern 00000000
[    0.000000]   0000000000 - 0000004000 pattern ffffffff
[    0.000000]   0000000000 - 0000004000 pattern 55555555
[    0.000000]   0000000000 - 0000004000 pattern aaaaaaaa
[    0.000000] early_memtest: wipe out test pattern from memory
[    0.000000]   0000000000 - 0000004000 pattern 00000000
[    0.000000] early_memtest: # of tests: 4
[    0.000000]   000054c000 - 0006300000 pattern 00000000
[    0.000000]   000054c000 - 0006300000 pattern ffffffff
[    0.000000]   000054c000 - 0006300000 pattern 55555555
[    0.000000]   000054c000 - 0006300000 pattern aaaaaaaa
[    0.000000] early_memtest: wipe out test pattern from memory
[    0.000000]   000054c000 - 0006300000 pattern 00000000
[    0.000000] early_memtest: # of tests: 4
[    0.000000]   0006400000 - 0007ffb000 pattern 00000000
[    0.000000]   0006400000 - 0007ffb000 pattern ffffffff
[    0.000000]   0006400000 - 0007ffb000 pattern 55555555
[    0.000000]   0006400000 - 0007ffb000 pattern aaaaaaaa
[    0.000000] early_memtest: wipe out test pattern from memory
[    0.000000]   0006400000 - 0007ffb000 pattern 00000000
[    0.000000] Memory: 99MB 28MB = 127MB total
[    0.000000] Memory: 124624k/124624k available, 5424k reserved, 0K highmem
--------- with hole (mem=99M@0x0 mem=28M@0x6400000) ---------


From 3034a0d2fc71c8edef43d0d04b3be0ffad484fca Mon Sep 17 00:00:00 2001
From: Alexander Holler <hol...@ahsoftware.de>
Date: Wed, 31 Oct 2012 22:24:04 +0100
Subject: [PATCH] arm: add memtest

Signed-off-by: Alexander Holler <hol...@ahsoftware.de>
---
 arch/arm/mm/Kconfig     |   11 ++++
 arch/arm/mm/Makefile    |    2 +
 arch/arm/mm/init.c      |   36 +++++++++++++-
arch/arm/mm/memtest32.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 173 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/mm/memtest32.c

diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 101b968..b14941f 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -874,3 +874,14 @@ config ARCH_HAS_BARRIERS
        help
          This option allows the use of custom mandatory barriers
          included via the mach/barriers.h file.
+
+config MEMTEST
+       bool "Memtest"
+       ---help---
+         This option adds a kernel parameter 'memtest', which allows memtest
+         to be set.
+               memtest=0, mean disabled; -- default
+               memtest=1, mean do 1 test pattern;
+               ...
+               memtest=4, mean do 4 test patterns.
+         If you are unsure how to answer this question, answer N.
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index 8a9c4cb..8cbfda1 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -96,3 +96,5 @@ obj-$(CONFIG_CACHE_FEROCEON_L2)       += cache-feroceon-l2.o
 obj-$(CONFIG_CACHE_L2X0)       += cache-l2x0.o
 obj-$(CONFIG_CACHE_XSC3L2)     += cache-xsc3l2.o
 obj-$(CONFIG_CACHE_TAUROS2)    += cache-tauros2.o
+
+obj-$(CONFIG_MEMTEST)          += memtest32.o
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 9aec41f..0941946 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -584,6 +584,10 @@ static void __init free_highpages(void)
 #endif
 }

+#ifdef CONFIG_MEMTEST
+extern void early_memtest32(unsigned long start, unsigned long end);
+#endif
+
 /*
  * mem_init() marks the free areas in the mem_map and tells us how much
  * memory is free.  This is done after various parts of the system have
@@ -618,6 +622,9 @@ void __init mem_init(void)
        reserved_pages = free_pages = 0;

        for_each_bank(i, &meminfo) {
+#ifdef CONFIG_MEMTEST
+               phys_addr_t memtest_start = 0xffffffff, memtest_end;
+#endif
                struct membank *bank = &meminfo.bank[i];
                unsigned int pfn1, pfn2;
                struct page *page, *end;
@@ -629,12 +636,37 @@ void __init mem_init(void)
                end  = pfn_to_page(pfn2 - 1) + 1;

                do {
-                       if (PageReserved(page))
+                       if (PageReserved(page)) {
                                reserved_pages++;
-                       else if (!page_count(page))
+#ifdef CONFIG_MEMTEST
+                               /* something has cut a hole */
+                               if (memtest_start != 0xffffffff) {
+                                       early_memtest32(memtest_start,  
memtest_end);
+                                       memtest_start = 0xffffffff;
+                               }
+#endif
+                       } else if (!page_count(page)) {
                                free_pages++;
+#ifdef CONFIG_MEMTEST
+                               if (memtest_start == 0xffffffff) {
+                                       /* start of a block for memtest */
+                                       memtest_start = page_to_phys(page);
+                               } else if (memtest_end != page_to_phys(page)) {
+                                       /* hole detected, call memtest */
+                                       early_memtest32(memtest_start,  
memtest_end);
+                                       /* and start with new values */
+                                       memtest_start = page_to_phys(page);
+                               }
+                               memtest_end = page_to_phys(page)+PAGE_SIZE;
+#endif
+                       }
                        page++;
                } while (page < end);
+#ifdef CONFIG_MEMTEST
+               if (memtest_start != 0xffffffff)
+                       early_memtest32(memtest_start,  memtest_end);
+               /* if bad memory was found, reserved_pages is wrong (without 
bad mem) */
+#endif
        }

        /*
diff --git a/arch/arm/mm/memtest32.c b/arch/arm/mm/memtest32.c
new file mode 100644
index 0000000..1564b5b
--- /dev/null
+++ b/arch/arm/mm/memtest32.c
@@ -0,0 +1,126 @@
+/* This is just a checkpatch'ed copy of arch/x86/mm/memtest.c modified to use 32bit */
+#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 <linux/memblock.h>
+
+static u32 patterns[] __initdata = {
+       0,
+       0xffffffffUL,
+       0x55555555UL,
+       0xaaaaaaaaUL,
+       0x11111111UL,
+       0x22222222UL,
+       0x44444444UL,
+       0x88888888UL,
+       0x33333333UL,
+       0x66666666UL,
+       0x99999999UL,
+       0xccccccccUL,
+       0x77777777UL,
+       0xbbbbbbbbUL,
+       0xddddddddUL,
+       0xeeeeeeeeUL,
+       0x7a6c7258UL, /* yeah ;-) */
+};
+
+static void __init reserve_bad_mem(u32 pattern, u32 start_bad, u32 end_bad)
+{
+       pr_info("  %08lx bad mem addr %010lx - %010lx reserved\n",
+              (unsigned long) pattern,
+              (unsigned long) start_bad,
+              (unsigned long) end_bad);
+       memblock_reserve(start_bad, end_bad - start_bad);
+}
+
+static void __init memtest(u32 pattern, u32 start_phys, u32 size)
+{
+       u32 *p, *start, *end;
+       u32 start_bad, last_bad;
+       u32 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(u32 pattern, u32 start, u32 end)
+{
+       u64 i;
+       phys_addr_t this_start, this_end;
+
+       for_each_free_mem_range(i, MAX_NUMNODES, &this_start, &this_end, NULL) {
+               this_start = clamp_t(phys_addr_t, this_start, start, end);
+               this_end = clamp_t(phys_addr_t, this_end, start, end);
+               if (this_start < this_end) {
+                       pr_info("  %010lx - %010lx pattern %08lx\n",
+                              (unsigned long)this_start,
+                              (unsigned long)this_end,
+                              (unsigned long)cpu_to_be32(pattern));
+                       memtest(pattern, this_start, this_end - this_start);
+               }
+       }
+}
+
+/* default is disabled */
+static int memtest_pattern __initdata;
+
+static int __init parse_memtest(char *arg)
+{
+       ssize_t ret __always_unused;
+
+       if (arg)
+               ret = kstrtoint(arg, 0, &memtest_pattern);
+       else
+               memtest_pattern = ARRAY_SIZE(patterns);
+
+       return 0;
+}
+
+early_param("memtest", parse_memtest);
+
+void __init early_memtest32(unsigned long start, unsigned long end)
+{
+       unsigned int i;
+       unsigned int idx = 0;
+
+       if (!memtest_pattern)
+               return;
+
+       pr_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) {
+               pr_info("early_memtest: wipe out test pattern from memory\n");
+               /* additional test with pattern 0 will do this */
+               do_one_pass(0, start, end);
+       }
+}
--
1.7.8.6


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to