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>
This commit is contained in:
Simon Glass 2020-06-02 19:26:49 -06:00 committed by Tom Rini
parent a33a824227
commit bdded2015c
6 changed files with 456 additions and 0 deletions

10
README
View file

@ -3237,6 +3237,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
@ -3482,6 +3483,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

View file

@ -718,6 +718,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

151
cmd/mem.c
View file

@ -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++;
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++;
}
/* 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

View file

@ -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

5
test/cmd/Makefile Normal file
View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (c) 2013 Google, Inc
obj-$(CONFIG_MEM_SEARCH) += mem_search.o

275
test/cmd/mem_search.c Normal file
View file

@ -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);