From mboxrd@z Thu Jan 1 00:00:00 1970 From: Stefan Roese Date: Wed, 3 Jun 2020 08:05:38 +0200 Subject: [PATCH 6/6] cmd: Add a memory-search command In-Reply-To: <20200602192642.6.Iaf24bd607de3836b8cd474fd0ae88452396ccb62@changeid> References: <20200602192642.1.Ibc067a3d1c5425aa60dfb9314a23d6dc5c2e480d@changeid> <20200602192642.6.Iaf24bd607de3836b8cd474fd0ae88452396ccb62@changeid> Message-ID: <8a4a362e-5e8a-57bd-dca9-e4bcebe6e5e5@denx.de> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de 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 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 > #include > #include > +#include > #include > > 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 | -] address #-of-objects ..." > + " -q = quiet, -l = 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 > + */ > + > +#include > +#include > +#include > +#include > +#include > + > +#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