mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 07:04:28 +00:00
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:
parent
a33a824227
commit
bdded2015c
6 changed files with 456 additions and 0 deletions
10
README
10
README
|
@ -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
|
||||
|
|
14
cmd/Kconfig
14
cmd/Kconfig
|
@ -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
151
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++;
|
||||
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
|
||||
|
|
|
@ -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
5
test/cmd/Makefile
Normal 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
275
test/cmd/mem_search.c
Normal 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);
|
||||
|
Loading…
Reference in a new issue