[PATCH 6/6] commands: add new memtest command
Alexander Aring
alex.aring at gmail.com
Thu Feb 7 05:45:00 EST 2013
Add new memtest command which can enable or disable caching
on non allocted barebox regions(test area).
This command simply parse and check parameters then call
the mem_test routine.
If no address parameters are given then mem_test will call
for each memory bank.
Signed-off-by: Alexander Aring <alex.aring at gmail.com>
---
commands/Kconfig | 10 ++
commands/Makefile | 1 +
commands/memtest.c | 362 +++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 373 insertions(+)
create mode 100644 commands/memtest.c
diff --git a/commands/Kconfig b/commands/Kconfig
index 7cc759c..d158c3f 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -516,6 +516,16 @@ config CMD_NANDTEST
select PARTITION_NEED_MTD
prompt "nandtest"
+config CMD_MEMTEST
+ tristate
+ select MEMTEST
+ prompt "memtest"
+ help
+ This command enables a memtest to test installed memory.
+ During this test allocated iomem regions will be skipped.
+ If tested architecture has MMU with PTE flags support,
+ caching can be set enabled or disabled.
+
endmenu
menu "video command"
diff --git a/commands/Makefile b/commands/Makefile
index 393ba51..b39b489 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_CMD_LOADY) += loadxy.o
obj-$(CONFIG_CMD_LOADS) += loads.o
obj-$(CONFIG_CMD_ECHO) += echo.o
obj-$(CONFIG_CMD_MEMORY) += mem.o
+obj-$(CONFIG_CMD_MEMTEST) += memtest.o
obj-$(CONFIG_CMD_EDIT) += edit.o
obj-$(CONFIG_CMD_EXEC) += exec.o
obj-$(CONFIG_CMD_SLEEP) += sleep.o
diff --git a/commands/memtest.c b/commands/memtest.c
new file mode 100644
index 0000000..22e8006
--- /dev/null
+++ b/commands/memtest.c
@@ -0,0 +1,362 @@
+/*
+ * memtest - Perform a memory test
+ *
+ * (C) Copyright 2013
+ * Alexander Aring <aar at pengutronix.de>, Pengutronix
+ *
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd at denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <command.h>
+#include <getopt.h>
+#include <asm/mmu.h>
+
+#include <memory_test.h>
+
+/*
+ * In CONFIG_MMU we have a special c flag.
+ */
+#ifdef CONFIG_MMU
+static char optstr[] = "s:e:i:cb";
+
+/*
+ * PTE flags variables to set cached and
+ * uncached regions.
+ */
+static uint32_t pte_flags_cached;
+static uint32_t pte_flags_uncached;
+#else
+static char optstr[] = "s:e:i:b";
+#endif
+
+#ifdef CONFIG_MMU
+static void print_region(vu_long start, vu_long size, uint32_t flags)
+{
+ if (!size)
+ return;
+
+ printf("\t0x%08lx - "
+ "0x%08lx (size 0x%08lx)\n",
+ start, start + size - 1, size);
+}
+
+static void do_remap_range(struct memory_bank *bank, uint32_t flags)
+{
+ struct resource *r = NULL;
+ struct resource *r_prev = NULL;
+
+ vu_long size;
+ vu_long start;
+ vu_long end;
+
+ if (flags == pte_flags_uncached)
+ printf("Set non caching regions:\n");
+ else if (flags == pte_flags_cached)
+ printf("Set caching regions:\n");
+ else
+ BUG();
+
+ /*
+ * We assume that the regions are sorted in this list
+ */
+ list_for_each_entry(r, &bank->res->children, sibling) {
+ /*
+ * Do on head element for bank boundary
+ */
+ if (r->sibling.prev == &bank->res->children) {
+ /*
+ * remember last used element
+ */
+ r_prev = r;
+
+ start = PAGE_ALIGN(bank->start);
+ end = PAGE_ALIGN_DOWN(r->start) - 1;
+ if (start >= end)
+ continue;
+ size = end - start + 1;
+
+ print_region(start, size, flags);
+ remap_range((void *)start, size, flags);
+
+ continue;
+ }
+ /*
+ * Between used regions
+ */
+ start = PAGE_ALIGN(r_prev->end);
+ end = PAGE_ALIGN_DOWN(r->start) - 1;
+ if (start < end) {
+ size = end - start + 1;
+ print_region(start, size, flags);
+ remap_range((void *)start, size, flags);
+ }
+
+ r_prev = r;
+ /*
+ * Do on head element for bank boundary
+ */
+ if (list_is_last(&r->sibling, &bank->res->children)) {
+ start = PAGE_ALIGN(r->end);
+ end = PAGE_ALIGN_DOWN(bank->start + bank->size) - 1;
+ if (start >= end)
+ continue;
+ size = end - start + 1;
+
+ print_region(start, size, flags);
+ remap_range((void *)start, size, flags);
+ }
+ }
+}
+#endif
+
+static int do_mem_memtest(int argc, char *argv[])
+{
+ /*
+ * Set start address to 0xffffffff which
+ * can't be.
+ */
+ vu_long start = 0xffffffff;
+ vu_long end = 0;
+
+ uint i;
+ uint max_i = 1;
+
+#ifdef CONFIG_MMU
+ int cache = 0;
+#endif
+ int bus_only = 0;
+ int err = 0;
+ int cnt = 0;
+ int opt;
+
+ struct memory_bank *bank = NULL;
+ struct resource *r = NULL;
+
+ while ((opt = getopt(argc, argv, optstr)) > 0) {
+ switch (opt) {
+ case 's':
+ start = simple_strtoul(optarg, NULL, 0);
+ break;
+ case 'e':
+ end = simple_strtoul(optarg, NULL, 0);
+ break;
+ case 'i':
+ max_i = simple_strtoul(optarg, NULL, 0);
+ break;
+#ifdef CONFIG_MMU
+ case 'c':
+ cache = 1;
+ break;
+#endif
+ case 'b':
+ bus_only = 1;
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ if (optind > argc)
+ return COMMAND_ERROR_USAGE;
+
+ /*
+ * Error if no end address
+ */
+ if (start != 0xffffffff && !end) {
+ printf("Please add an end address.\n");
+ return 1;
+ }
+
+ /*
+ * Error if no start address
+ */
+ if (end && start == 0xffffffff) {
+ printf("Please add a start address.\n");
+ return 1;
+ }
+
+ /*
+ * Check parameters
+ */
+ if (start != 0xffffffff && end) {
+ if (end <= start) {
+ printf("End address less than or"
+ " equal start address.\n");
+ return 1;
+ }
+
+ /*
+ * Check if given start and end address are in any banks
+ */
+ for_each_memory_bank(bank) {
+ if (ADDRESS_IN_REGIONS(start, bank->start,
+ bank->start + bank->size))
+ cnt++;
+
+ if (ADDRESS_IN_REGIONS(end, bank->start,
+ bank->start + bank->size))
+ cnt++;
+ }
+
+ if (cnt != 2) {
+ printf("Start or end addresses are"
+ " not in any ram bank.\n");
+ return 1;
+ }
+ }
+
+#ifdef CONFIG_MMU
+ /*
+ * Get pte flags. Which are configured at
+ * runtime at booting.
+ */
+ pte_flags_cached = mmu_get_pte_cached_flags();
+ pte_flags_uncached = mmu_get_pte_uncached_flags();
+#endif
+
+ printf("Skipping regions:\n");
+ for_each_memory_bank(bank) {
+ list_for_each_entry(r, &bank->res->children, sibling)
+ printf("\t0x%08x - "
+ "0x%08x (size 0x%08x) %s\n",
+ r->start, r->end,
+ r->end - r->start + 1, r->name);
+#ifdef CONFIG_MMU
+ /*
+ * Disable or enable caching
+ */
+ if (cache)
+ do_remap_range(bank, pte_flags_cached);
+ else
+ do_remap_range(bank, pte_flags_uncached);
+#endif
+ }
+
+ /*
+ * Do test if we set a start or end address
+ */
+ if (start != 0xffffffff && end) {
+ printf("Testing address range:\n\t0x%08lx - 0x%08lx"
+ " (size 0x%08lx)\n",
+ start, end, end - start + 1);
+
+ for (i = 1; (i <= max_i) || !max_i; i++) {
+ printf("Iteration: %u\n", i);
+
+ /*
+ * Do the Memtest
+ */
+ err = mem_test(start, end, bus_only);
+ if (err == -EINTR) {
+ printf("Test interrupted.\n");
+ goto err;
+ }
+
+ if (err < 0) {
+ printf("Test failed.\n");
+ goto err;
+ }
+ printf("Tested %u iteration(s) without errors.\n", i);
+ }
+#ifdef CONFIG_MMU
+ /*
+ * Renable caching
+ */
+ if (!cache)
+ for_each_memory_bank(bank)
+ do_remap_range(bank, pte_flags_cached);
+#endif
+ printf("Memtest done.\n");
+
+ return 0;
+ }
+
+ /*
+ * If we set no start or end address
+ * we do the test on all ram banks
+ */
+ for (i = 1; (i <= max_i) || !max_i; i++) {
+ for_each_memory_bank(bank) {
+ start = bank->start;
+ end = bank->start + bank->size - 1;
+
+ printf("Iteration: %u\n", i);
+
+ printf("Testing address range:\n\t0x%08lx - "
+ "0x%08lx (size 0x%08lx) on bank /dev/%s\n",
+ start, end, bank->size,
+ bank->res->name);
+
+ err = mem_test(start, end, bus_only);
+ if (err == -EINTR) {
+ printf("Test interrupted.\n");
+ goto err;
+ }
+
+ if (err < 0) {
+ printf("Test on bank /dev/%s failed.\n",
+ bank->res->name);
+ goto err;
+ }
+ printf("Tested %u iteration(s) without errors.\n", i);
+ }
+ }
+#ifdef CONFIG_MMU
+ /*
+ * Renable caching
+ */
+ if (!cache)
+ for_each_memory_bank(bank)
+ do_remap_range(bank, pte_flags_cached);
+#endif
+ printf("Memtest done.\n");
+
+ return 0;
+
+err:
+#ifdef CONFIG_MMU
+ /*
+ * Enable caching
+ */
+ for_each_memory_bank(bank)
+ do_remap_range(bank, pte_flags_cached);
+#endif
+
+ return 1;
+}
+
+static const __maybe_unused char cmd_memtest_help[] =
+"Usage: memtest [OPTION]...\n"
+"memtest related commands\n"
+" -s <start> start address to begin memtest.\n"
+" -e <end> end address to stop memtest.\n"
+" -i <iterations> iterations [default=1, endless=0].\n"
+#ifdef CONFIG_MMU
+" -c run test with enable cache.\n"
+#endif
+" -b only test bus datalines.";
+
+BAREBOX_CMD_START(memtest)
+ .cmd = do_mem_memtest,
+ .usage = "Memory Test",
+ BAREBOX_CMD_HELP(cmd_memtest_help)
+BAREBOX_CMD_END
--
1.8.1.2
More information about the barebox
mailing list