[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