Skip to content

Commit 510dca5

Browse files
Chris Friedtcarlescufi
authored andcommitted
shell: devmem: add devmem dump subcommand
This allows the caller to dump a region of memory rather than dumping one byte (word, etc) at a time. Additionally, it respects alignment requirements so it works for e.g. 32-bit register accesses. Signed-off-by: Chris Friedt <[email protected]>
1 parent 9b43e3a commit 510dca5

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed

subsys/shell/modules/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ config DATE_SHELL
4242
config DEVMEM_SHELL
4343
bool "Devmem shell"
4444
default y if !SHELL_MINIMAL
45+
select GETOPT
4546
help
4647
This shell command provides read/write access to physical memory.

subsys/shell/modules/devmem_service.c

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Copyright (c) 2020 Intel Corporation
33
* Copyright (c) 2021 Antmicro <www.antmicro.com>
4+
* Copyright (c) 2022 Meta
45
*
56
* SPDX-License-Identifier: Apache-2.0
67
*/
@@ -9,6 +10,11 @@
910
#include <zephyr/device.h>
1011
#include <zephyr/shell/shell.h>
1112
#include <zephyr/sys/byteorder.h>
13+
#ifdef CONFIG_ARCH_POSIX
14+
#include <unistd.h>
15+
#else
16+
#include <zephyr/posix/unistd.h>
17+
#endif
1218

1319
static inline bool is_ascii(uint8_t data)
1420
{
@@ -26,6 +32,122 @@ static bool littleendian;
2632
#define CHAR_CAN 0x18
2733
#define CHAR_DC1 0x11
2834

35+
#ifndef BITS_PER_BYTE
36+
#define BITS_PER_BYTE 8
37+
#endif
38+
39+
static int memory_dump(const struct shell *sh, mem_addr_t phys_addr, size_t size, uint8_t width)
40+
{
41+
uint32_t value;
42+
size_t data_offset;
43+
mm_reg_t addr;
44+
const size_t vsize = width / BITS_PER_BYTE;
45+
uint8_t data[SHELL_HEXDUMP_BYTES_IN_LINE];
46+
47+
#if defined(CONFIG_MMU) || defined(CONFIG_PCIE)
48+
device_map((mm_reg_t *)&addr, phys_addr, size, K_MEM_CACHE_NONE);
49+
50+
shell_print(sh, "Mapped 0x%lx to 0x%lx\n", phys_addr, addr);
51+
#else
52+
addr = phys_addr;
53+
#endif /* defined(CONFIG_MMU) || defined(CONFIG_PCIE) */
54+
55+
for (; size > 0;
56+
addr += SHELL_HEXDUMP_BYTES_IN_LINE, size -= MIN(size, SHELL_HEXDUMP_BYTES_IN_LINE)) {
57+
for (data_offset = 0;
58+
size >= vsize && data_offset + vsize <= SHELL_HEXDUMP_BYTES_IN_LINE;
59+
data_offset += vsize) {
60+
switch (width) {
61+
case 8:
62+
value = sys_read8(addr + data_offset);
63+
data[data_offset] = value;
64+
break;
65+
case 16:
66+
value = sys_read16(addr + data_offset);
67+
if (IS_ENABLED(CONFIG_BIG_ENDIAN)) {
68+
value = __bswap_16(value);
69+
}
70+
71+
data[data_offset] = (uint8_t)value;
72+
value >>= 8;
73+
data[data_offset + 1] = (uint8_t)value;
74+
break;
75+
case 32:
76+
value = sys_read32(addr + data_offset);
77+
if (IS_ENABLED(CONFIG_BIG_ENDIAN)) {
78+
value = __bswap_32(value);
79+
}
80+
81+
data[data_offset] = (uint8_t)value;
82+
value >>= 8;
83+
data[data_offset + 1] = (uint8_t)value;
84+
value >>= 8;
85+
data[data_offset + 2] = (uint8_t)value;
86+
value >>= 8;
87+
data[data_offset + 3] = (uint8_t)value;
88+
break;
89+
default:
90+
shell_fprintf(sh, SHELL_NORMAL, "Incorrect data width\n");
91+
return -EINVAL;
92+
}
93+
}
94+
95+
shell_hexdump_line(sh, addr, data, MIN(size, SHELL_HEXDUMP_BYTES_IN_LINE));
96+
}
97+
98+
return 0;
99+
}
100+
101+
static int cmd_dump(const struct shell *sh, size_t argc, char **argv)
102+
{
103+
int rv;
104+
size_t size = -1;
105+
size_t width = 32;
106+
mem_addr_t addr = -1;
107+
108+
optind = 1;
109+
while ((rv = getopt(argc, argv, "a:s:w:")) != -1) {
110+
switch (rv) {
111+
case 'a':
112+
addr = (mem_addr_t)strtoul(optarg, NULL, 16);
113+
if (addr == 0 && errno == EINVAL) {
114+
shell_error(sh, "invalid addr '%s'", optarg);
115+
return -EINVAL;
116+
}
117+
break;
118+
case 's':
119+
size = (size_t)strtoul(optarg, NULL, 0);
120+
if (size == 0 && errno == EINVAL) {
121+
shell_error(sh, "invalid size '%s'", optarg);
122+
return -EINVAL;
123+
}
124+
break;
125+
case 'w':
126+
width = (size_t)strtoul(optarg, NULL, 0);
127+
if (width == 0 && errno == EINVAL) {
128+
shell_error(sh, "invalid width '%s'", optarg);
129+
return -EINVAL;
130+
}
131+
break;
132+
case '?':
133+
default:
134+
return -EINVAL;
135+
}
136+
}
137+
138+
if (addr == -1) {
139+
shell_error(sh, "'-a <address>' is mandatory");
140+
return -EINVAL;
141+
}
142+
143+
if (size == -1) {
144+
shell_error(sh, "'-s <size>' is mandatory");
145+
return -EINVAL;
146+
}
147+
148+
return memory_dump(sh, addr, size, width);
149+
}
150+
29151
static int set_bypass(const struct shell *sh, shell_bypass_cb_t bypass)
30152
{
31153
static bool in_use;
@@ -229,6 +351,10 @@ static int cmd_devmem(const struct shell *sh, size_t argc, char **argv)
229351
}
230352

231353
SHELL_STATIC_SUBCMD_SET_CREATE(sub_devmem,
354+
SHELL_CMD_ARG(dump, NULL,
355+
"Usage:\n"
356+
"devmem dump -a <address> -s <size> [-w <width>]\n",
357+
cmd_dump, 4, 6),
232358
SHELL_CMD_ARG(load, NULL,
233359
"Usage:\n"
234360
"devmem load [options] [address]\n"

0 commit comments

Comments
 (0)