cmd: Add a memory-search command

It is useful to be able to find hex values and strings in a memory range.
Add a command to support this.

cmd: Fix 'md' and add a memory-search command
At present 'md.q' is broken. This series provides a fix for this. It also
implements a new memory-search command called 'ms'. It allows searching
memory for hex and string data.
END

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/test/Makefile b/test/Makefile
index bab8f1a..7c40399 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -3,6 +3,7 @@
 # (C) Copyright 2012 The Chromium Authors
 
 obj-$(CONFIG_SANDBOX) += bloblist.o
+obj-$(CONFIG_CMDLINE) += cmd/
 obj-$(CONFIG_UNIT_TEST) += cmd_ut.o
 obj-$(CONFIG_UNIT_TEST) += ut.o
 obj-$(CONFIG_SANDBOX) += command_ut.o
diff --git a/test/cmd/Makefile b/test/cmd/Makefile
new file mode 100644
index 0000000..85d38f0
--- /dev/null
+++ b/test/cmd/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2013 Google, Inc
+
+obj-$(CONFIG_MEM_SEARCH) += mem_search.o
diff --git a/test/cmd/mem_search.c b/test/cmd/mem_search.c
new file mode 100644
index 0000000..d57bfad
--- /dev/null
+++ b/test/cmd/mem_search.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for memory commands
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <console.h>
+#include <mapmem.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+#define BUF_SIZE	0x100
+
+/* Test 'ms' command with bytes */
+static int dm_test_ms_b(struct unit_test_state *uts)
+{
+	u8 *buf;
+
+	buf = map_sysmem(0, BUF_SIZE + 1);
+	memset(buf, '\0', BUF_SIZE);
+	buf[0x0] = 0x12;
+	buf[0x31] = 0x12;
+	buf[0xff] = 0x12;
+	buf[0x100] = 0x12;
+	console_record_reset();
+	run_command("ms.b 1 ff 12", 0);
+	ut_assert_nextline("00000030: 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................");
+	ut_assert_nextline("--");
+	ut_assert_nextline("000000f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12    ................");
+	ut_assert_nextline("2 matches");
+	ut_assert_console_end();
+
+	ut_asserteq(2, env_get_hex("memmatches", 0));
+	ut_asserteq(0xff, env_get_hex("memaddr", 0));
+	ut_asserteq(0xfe, env_get_hex("mempos", 0));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+DM_TEST(dm_test_ms_b, 0);
+
+/* Test 'ms' command with 16-bit values */
+static int dm_test_ms_w(struct unit_test_state *uts)
+{
+	u16 *buf;
+
+	buf = map_sysmem(0, BUF_SIZE + 2);
+	memset(buf, '\0', BUF_SIZE);
+	buf[0x34 / 2] = 0x1234;
+	buf[BUF_SIZE / 2] = 0x1234;
+	console_record_reset();
+	run_command("ms.w 0 80 1234", 0);
+	ut_assert_nextline("00000030: 0000 0000 1234 0000 0000 0000 0000 0000    ....4...........");
+	ut_assert_nextline("1 match");
+	ut_assert_console_end();
+
+	ut_asserteq(1, env_get_hex("memmatches", 0));
+	ut_asserteq(0x34, env_get_hex("memaddr", 0));
+	ut_asserteq(0x34 / 2, env_get_hex("mempos", 0));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+DM_TEST(dm_test_ms_w, 0);
+
+/* Test 'ms' command with 32-bit values */
+static int dm_test_ms_l(struct unit_test_state *uts)
+{
+	u32 *buf;
+
+	buf = map_sysmem(0, BUF_SIZE + 4);
+	memset(buf, '\0', BUF_SIZE);
+	buf[0x38 / 4] = 0x12345678;
+	buf[BUF_SIZE / 4] = 0x12345678;
+	console_record_reset();
+	run_command("ms 0 40 12345678", 0);
+	ut_assert_nextline("00000030: 00000000 00000000 12345678 00000000    ........xV4.....");
+	ut_assert_nextline("1 match");
+	ut_assert_console_end();
+
+	ut_asserteq(1, env_get_hex("memmatches", 0));
+	ut_asserteq(0x38, env_get_hex("memaddr", 0));
+	ut_asserteq(0x38 / 4, env_get_hex("mempos", 0));
+
+	console_record_reset();
+	run_command("ms 0 80 12345679", 0);
+	ut_assert_nextline("0 matches");
+	ut_assert_console_end();
+
+	ut_asserteq(0, env_get_hex("memmatches", 0));
+	ut_asserteq(0, env_get_hex("memaddr", 0));
+	ut_asserteq(0 / 4, env_get_hex("mempos", 0));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+DM_TEST(dm_test_ms_l, 0);
+
+/* Test 'ms' command with continuation */
+static int dm_test_ms_cont(struct unit_test_state *uts)
+{
+	char *const args[] = {"ms.b", "0", "100", "34"};
+	int repeatable;
+	u8 *buf;
+	int i;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	memset(buf, '\0', BUF_SIZE);
+	for (i = 5; i < 0x33; i += 3)
+		buf[i] = 0x34;
+	console_record_reset();
+	run_command("ms.b 0 100 34", 0);
+	ut_assert_nextlinen("00000000: 00 00 00 00 00 34 00 00 34 00 00 34 00 00 34 00");
+	ut_assert_nextline("--");
+	ut_assert_nextlinen("00000010: 00 34 00 00 34 00 00 34 00 00 34 00 00 34 00 00");
+	ut_assert_nextline("--");
+	ut_assert_nextlinen("00000020: 34 00 00 34 00 00 34 00 00 34 00 00 34 00 00 34");
+	ut_assert_nextlinen("10 matches (repeat command to check for more)");
+	ut_assert_console_end();
+
+	ut_asserteq(10, env_get_hex("memmatches", 0));
+	ut_asserteq(0x20, env_get_hex("memaddr", 0));
+	ut_asserteq(0x20, env_get_hex("mempos", 0));
+
+	/*
+	 * run_command() ignoes the repeatable flag when using hush, so call
+	 * cmd_process() directly
+	 */
+	console_record_reset();
+	cmd_process(CMD_FLAG_REPEAT, 4, args, &repeatable, NULL);
+	ut_assert_nextlinen("00000020: 34 00 00 34 00 00 34 00 00 34 00 00 34 00 00 34");
+	ut_assert_nextline("--");
+	ut_assert_nextlinen("00000030: 00 00 34 00 00 00 00 00");
+	ut_assert_nextlinen("6 matches");
+	ut_assert_console_end();
+
+	ut_asserteq(6, env_get_hex("memmatches", 0));
+	ut_asserteq(0x32, env_get_hex("memaddr", 0));
+
+	/* 0x32 less 0x21, where the second search started */
+	ut_asserteq(0x11, env_get_hex("mempos", 0));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+DM_TEST(dm_test_ms_cont, 0);
+
+/* Test 'ms' command with multiple values */
+static int dm_test_ms_mult(struct unit_test_state *uts)
+{
+	static const char str[] = "hello";
+	char *buf;
+
+	buf = map_sysmem(0, BUF_SIZE + 5);
+	memset(buf, '\0', BUF_SIZE);
+	strcpy(buf + 0x1e, str);
+	strcpy(buf + 0x63, str);
+	strcpy(buf + BUF_SIZE - strlen(str) + 1, str);
+	console_record_reset();
+	run_command("ms.b 0 100 68 65 6c 6c 6f", 0);
+	ut_assert_nextline("00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 68 65    ..............he");
+	ut_assert_nextline("00000020: 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 00 00    llo.............");
+	ut_assert_nextline("--");
+	ut_assert_nextline("00000060: 00 00 00 68 65 6c 6c 6f 00 00 00 00 00 00 00 00    ...hello........");
+	ut_assert_nextline("2 matches");
+	ut_assert_console_end();
+	unmap_sysmem(buf);
+
+	ut_asserteq(2, env_get_hex("memmatches", 0));
+	ut_asserteq(0x63, env_get_hex("memaddr", 0));
+	ut_asserteq(0x63, env_get_hex("mempos", 0));
+
+	return 0;
+}
+DM_TEST(dm_test_ms_mult, 0);
+
+/* Test 'ms' command with string */
+static int dm_test_ms_s(struct unit_test_state *uts)
+{
+	static const char str[] = "hello";
+	static const char str2[] = "hellothere";
+	char *buf;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	memset(buf, '\0', BUF_SIZE);
+	strcpy(buf + 0x1e, str);
+	strcpy(buf + 0x63, str);
+	strcpy(buf + 0xa1, str2);
+	console_record_reset();
+	run_command("ms.s 0 100 hello", 0);
+	ut_assert_nextline("00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 68 65    ..............he");
+	ut_assert_nextline("00000020: 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 00 00    llo.............");
+	ut_assert_nextline("--");
+	ut_assert_nextline("00000060: 00 00 00 68 65 6c 6c 6f 00 00 00 00 00 00 00 00    ...hello........");
+	ut_assert_nextline("--");
+	ut_assert_nextline("000000a0: 00 68 65 6c 6c 6f 74 68 65 72 65 00 00 00 00 00    .hellothere.....");
+	ut_assert_nextline("3 matches");
+	ut_assert_console_end();
+
+	ut_asserteq(3, env_get_hex("memmatches", 0));
+	ut_asserteq(0xa1, env_get_hex("memaddr", 0));
+	ut_asserteq(0xa1, env_get_hex("mempos", 0));
+
+	console_record_reset();
+	run_command("ms.s 0 100 hello there", 0);
+	ut_assert_nextline("000000a0: 00 68 65 6c 6c 6f 74 68 65 72 65 00 00 00 00 00    .hellothere.....");
+	ut_assert_nextline("1 match");
+	ut_assert_console_end();
+
+	ut_asserteq(1, env_get_hex("memmatches", 0));
+	ut_asserteq(0xa1, env_get_hex("memaddr", 0));
+	ut_asserteq(0xa1, env_get_hex("mempos", 0));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+DM_TEST(dm_test_ms_s, 0);
+
+/* Test 'ms' command with limit */
+static int dm_test_ms_limit(struct unit_test_state *uts)
+{
+	u8 *buf;
+
+	buf = map_sysmem(0, BUF_SIZE + 1);
+	memset(buf, '\0', BUF_SIZE);
+	buf[0x0] = 0x12;
+	buf[0x31] = 0x12;
+	buf[0x62] = 0x12;
+	buf[0x76] = 0x12;
+	console_record_reset();
+	run_command("ms.b -l2 1 ff 12", 0);
+	ut_assert_nextline("00000030: 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................");
+	ut_assert_nextline("--");
+	ut_assert_nextlinen("00000060: 00 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00");
+	ut_assert_nextline("2 matches (repeat command to check for more)");
+	ut_assert_console_end();
+
+	ut_asserteq(2, env_get_hex("memmatches", 0));
+	ut_asserteq(0x62, env_get_hex("memaddr", 0));
+	ut_asserteq(0x61, env_get_hex("mempos", 0));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+DM_TEST(dm_test_ms_limit, 0);
+
+/* Test 'ms' command in quiet mode */
+static int dm_test_ms_quiet(struct unit_test_state *uts)
+{
+	u8 *buf;
+
+	buf = map_sysmem(0, BUF_SIZE + 1);
+	memset(buf, '\0', BUF_SIZE);
+	buf[0x0] = 0x12;
+	buf[0x31] = 0x12;
+	buf[0x62] = 0x12;
+	buf[0x76] = 0x12;
+	console_record_reset();
+	run_command("ms.b -l2 1 ff 12", 0);
+	ut_assert_console_end();
+	unmap_sysmem(buf);
+
+	return 0;
+}
+DM_TEST(dm_test_ms_quiet, 0);
+