[PATCH 6/6] cmd: Add a memory-search command

Stefan Roese sr at denx.de
Wed Jun 3 08:05:38 CEST 2020


On 03.06.20 03:26, Simon Glass wrote:
> 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.

Perhaps this commit text belongs to a patchset cover letter? It does
not really match this explicit patch.

BTW: Why is md.q broken? I'm using it on my Octeon 64bit platform
without any issues so far.

> 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 at chromium.org>

Some minor review comments below.

> ---
> 
>   README                |  10 ++
>   cmd/Kconfig           |  14 +++
>   cmd/mem.c             | 151 +++++++++++++++++++++++
>   test/Makefile         |   1 +
>   test/cmd/Makefile     |   5 +
>   test/cmd/mem_search.c | 275 ++++++++++++++++++++++++++++++++++++++++++
>   6 files changed, 456 insertions(+)
>   create mode 100644 test/cmd/Makefile
>   create mode 100644 test/cmd/mem_search.c
> 
> diff --git a/README b/README
> index 17dc0ee33b..8abcff0783 100644
> --- a/README
> +++ b/README
> @@ -3283,6 +3283,7 @@ md	- memory display
>   mm	- memory modify (auto-incrementing)
>   nm	- memory modify (constant address)
>   mw	- memory write (fill)
> +ms	- memory search
>   cp	- memory copy
>   cmp	- memory compare
>   crc32	- checksum calculation
> @@ -3528,6 +3529,15 @@ List of environment variables (most likely not complete):
>   		  CONFIG_NET_RETRY_COUNT, if defined. This value has
>   		  precedence over the valu based on CONFIG_NET_RETRY_COUNT.
>   
> +  memmatches	- Number of matches found by the last 'ms' command, in hex
> +
> +  memaddr	- Address of the last match found by the 'ms' command, in hex,
> +		  or 0 if none
> +
> +  mempos	- Index position of the last match found by the 'ms' command,
> +		  in units of the size (.b, .w, .l) of the search
> +
> +
>   The following image location variables contain the location of images
>   used in booting. The "Image" column gives the role of the image and is
>   not an environment variable name. The other columns are environment
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 153864c587..a02a376d49 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -717,6 +717,20 @@ config CMD_MEMORY
>   	    base - print or set address offset
>   	    loop - initialize loop on address range
>   
> +config MEM_SEARCH
> +	bool "ms - Memory search"
> +	help
> +	  Memory-search command
> +
> +	  This allows searching through a region of memory looking for hex
> +	  data (byte, 16-bit word, 32-bit long, also 64-bit on machines that
> +	  support it). It is also possible to search for a string. The
> +	  command accepts a memory range and a list of values to search for.
> +	  The values need to appear in memory in the same order they are given
> +	  in the command. At most 10 matches can be returned at a time, but
> +	  pressing return will show the next 10 matches. Environment variables
> +	  are set for use with scripting (memmatches, memaddr, mempos).
> +
>   config CMD_MX_CYCLIC
>   	bool "Enable cyclic md/mw commands"
>   	depends on CMD_MEMORY
> diff --git a/cmd/mem.c b/cmd/mem.c
> index 9ab6b1dd08..575893c18d 100644
> --- a/cmd/mem.c
> +++ b/cmd/mem.c
> @@ -25,6 +25,7 @@
>   #include <asm/io.h>
>   #include <linux/bitops.h>
>   #include <linux/compiler.h>
> +#include <linux/ctype.h>
>   #include <linux/delay.h>
>   
>   DECLARE_GLOBAL_DATA_PTR;
> @@ -52,6 +53,10 @@ static ulong	dp_last_length = 0x40;
>   static ulong	mm_last_addr, mm_last_size;
>   
>   static	ulong	base_address = 0;
> +#ifdef CONFIG_MEM_SEARCH
> +static u8 search_buf[64];
> +static uint search_len;
> +#endif
>   
>   /* Memory Display
>    *
> @@ -362,6 +367,142 @@ static int do_mem_cp(struct cmd_tbl *cmdtp, int flag, int argc,
>   	return 0;
>   }
>   
> +#ifdef CONFIG_MEM_SEARCH
> +static int do_mem_search(struct cmd_tbl *cmdtp, int flag, int argc,
> +			 char *const argv[])
> +{
> +	ulong addr, length, bytes, offset;
> +	u8 *ptr, *end, *buf;
> +	bool quiet = false;
> +	ulong last_pos;		/* Offset of last match in 'size' units*/
> +	ulong last_addr;	/* Address of last displayed line */
> +	int limit = 10;
> +	int count;
> +	int size;
> +	int i;
> +
> +	/* We use the last specified parameters, unless new ones are entered */
> +	addr = dp_last_addr;
> +	size = dp_last_size;
> +	length = dp_last_length;
> +
> +	if (argc < 3)
> +		return CMD_RET_USAGE;
> +
> +	if ((!flag & CMD_FLAG_REPEAT)) {
> +		/*
> +		 * Check for a size specification.
> +		 * Defaults to long if no or incorrect specification.
> +		 */
> +		size = cmd_get_data_size(argv[0], 4);
> +		if (size < 0 && size != -2 /* string */)
> +			return 1;
> +
> +		argc--; argv++;

Two assignments in one line? ...

> +		while (argc && *argv[0] == '-') {
> +			int ch = argv[0][1];
> +
> +			if (ch == 'q')
> +				quiet = true;
> +			else if (ch == 'l' && isxdigit(argv[0][2]))
> +				limit = simple_strtoul(argv[0] + 2, NULL, 16);
> +			else
> +				return CMD_RET_USAGE;
> +			argc--; argv++;

... here as well.

Thanks,
Stefan

> +		}
> +
> +		/* Address is specified since argc > 1 */
> +		addr = simple_strtoul(argv[0], NULL, 16);
> +		addr += base_address;
> +
> +		/* Length is the number of objects, not number of bytes */
> +		length = simple_strtoul(argv[1], NULL, 16);
> +
> +		/* Read the bytes to search for */
> +		end = search_buf + sizeof(search_buf);
> +		for (i = 2, ptr = search_buf; i < argc && ptr < end; i++) {
> +			if (SUPPORT_64BIT_DATA && size == 8) {
> +				u64 val = simple_strtoull(argv[i], NULL, 16);
> +
> +				*(u64 *)ptr = val;
> +			} else if (size == -2) {  /* string */
> +				int len = min(strlen(argv[i]),
> +					      (size_t)(end - ptr));
> +
> +				memcpy(ptr, argv[i], len);
> +				ptr += len;
> +				continue;
> +			} else {
> +				u32 val = simple_strtoul(argv[i], NULL, 16);
> +
> +				switch (size) {
> +				case 1:
> +					*ptr = val;
> +					break;
> +				case 2:
> +					*(u16 *)ptr = val;
> +					break;
> +				case 4:
> +					*(u32 *)ptr = val;
> +					break;
> +				}
> +			}
> +			ptr += size;
> +		}
> +		search_len = ptr - search_buf;
> +	}
> +
> +	/* Do the search */
> +	if (size == -2)
> +		size = 1;
> +	bytes = size * length;
> +	buf = map_sysmem(addr, bytes);
> +	last_pos = 0;
> +	last_addr = 0;
> +	count = 0;
> +	for (offset = 0; offset <= bytes - search_len && count < limit;
> +	     offset += size) {
> +		void *ptr = buf + offset;
> +
> +		if (!memcmp(ptr, search_buf, search_len)) {
> +			uint align = (addr + offset) & 0xf;
> +			ulong match = addr + offset;
> +
> +			if (!count || (last_addr & ~0xf) != (match & ~0xf)) {
> +				if (!quiet) {
> +					if (count)
> +						printf("--\n");
> +					print_buffer(match - align, ptr - align,
> +						     size,
> +						     ALIGN(search_len + align,
> +							   16) / size, 0);
> +				}
> +				last_addr = match;
> +				last_pos = offset / size;
> +			}
> +			count++;
> +		}
> +	}
> +	if (!quiet) {
> +		printf("%d match%s", count, count == 1 ? "" : "es");
> +		if (count == limit)
> +			printf(" (repeat command to check for more)");
> +		printf("\n");
> +	}
> +	env_set_hex("memmatches", count);
> +	env_set_hex("memaddr", last_addr);
> +	env_set_hex("mempos", last_pos);
> +
> +	unmap_sysmem(buf);
> +
> +	dp_last_addr = addr + offset / size;
> +	dp_last_size = size;
> +	dp_last_length = length - offset / size;
> +
> +	return count ? 0 : CMD_RET_FAILURE;
> +}
> +#endif
> +
>   static int do_mem_base(struct cmd_tbl *cmdtp, int flag, int argc,
>   		       char *const argv[])
>   {
> @@ -1196,6 +1337,16 @@ U_BOOT_CMD(
>   	"[.b, .w, .l" HELP_Q "] addr1 addr2 count"
>   );
>   
> +#ifdef CONFIG_MEM_SEARCH
> +/**************************************************/
> +U_BOOT_CMD(
> +	ms,	255,	1,	do_mem_search,
> +	"memory search",
> +	"[.b, .w, .l" HELP_Q ", .s] [-q | -<n>] address #-of-objects <value>..."
> +	"  -q = quiet, -l<val> = match limit" :
> +);
> +#endif
> +
>   #ifdef CONFIG_CMD_CRC32
>   
>   #ifndef CONFIG_CRC32_VERIFY
> diff --git a/test/Makefile b/test/Makefile
> index bab8f1a5c2..7c4039964e 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 0000000000..85d38f09e8
> --- /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 0000000000..d57bfad398
> --- /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 at 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);
> +
> 


Viele Grüße,
Stefan

-- 
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de


More information about the U-Boot mailing list