shell: add devmem load command
This commit adds a devmem load command for shell that allows users to easily load arbitrary data into the device memory. Signed-off-by: Mateusz Sierszulski <msierszulski@internships.antmicro.com> Signed-off-by: Tomasz Gorochowik <tgorochowik@antmicro.com>
This commit is contained in:
parent
374aab1ea3
commit
9d62f8d8ac
6 changed files with 217 additions and 5 deletions
8
samples/subsys/shell/devmem_load/CMakeLists.txt
Normal file
8
samples/subsys/shell/devmem_load/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Copyright (c) 2021 Antmicro <www.antmicro.com>
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13.1)
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(devmem_load)
|
||||||
|
|
||||||
|
target_sources(app PRIVATE src/main.c)
|
53
samples/subsys/shell/devmem_load/README.md
Normal file
53
samples/subsys/shell/devmem_load/README.md
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# Zephyr devmem load command
|
||||||
|
This module add a `devmem load` command that allows data to be loaded into device memory.
|
||||||
|
The `devmem load` command is supported by every transport the shell can run on.
|
||||||
|
|
||||||
|
After using a command in the Zephyr shell, the device reads all transferred data and writes to an address in the memory.
|
||||||
|
The transfer ends when the user presses `ctrl-x + ctrl-q`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Note: when using the devmem load command over UART it is recommended to use interrupts whenever possible.
|
||||||
|
If this is not possible, reduce the baud rate to 9600.
|
||||||
|
|
||||||
|
If you use poll you should also use `prj_poll.conf` instead of `prj.conf`.
|
||||||
|
## Building
|
||||||
|
|
||||||
|
The sample can be built for several platforms, the following commands build and run the application with a shell for the FRDM-K64F board.
|
||||||
|
```bash
|
||||||
|
west build -b frdm_k64f samples/subsys/shell/devmem_load
|
||||||
|
west flash
|
||||||
|
```
|
||||||
|
|
||||||
|
Building for boards without UART interrupt support:
|
||||||
|
```bash
|
||||||
|
west build -b quick_feather -- -DOVERLAY_CONFIG=prj_poll.conf samples/subsys/shell/devmem_load
|
||||||
|
west flash
|
||||||
|
```
|
||||||
|
## Running
|
||||||
|
After connecting to the UART console you should see the following output:
|
||||||
|
```bash
|
||||||
|
uart:~$
|
||||||
|
```
|
||||||
|
The `devmem load` command can now be used (`devmem load [option] [address]`):
|
||||||
|
```bash
|
||||||
|
uart:~$ devmem load 0x20020000
|
||||||
|
Loading...
|
||||||
|
press ctrl-x ctrl-q to escape
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, the `devmem load` is waiting for data.
|
||||||
|
You can either type it directly from the console or send it from the host PC (replace `ttyX` with the appropriate one for your UART console):
|
||||||
|
```bash
|
||||||
|
xxd -p data > /dev/ttyX
|
||||||
|
```
|
||||||
|
(It is important to use plain-style hex dump)
|
||||||
|
Once the data is transferred, use `ctrl-x + ctrl-q` to quit the loader.
|
||||||
|
It will print the number of the bytes read and return to the shell:
|
||||||
|
```bash
|
||||||
|
Number of bytes read: 3442
|
||||||
|
uart:~$
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
Currently, the `devmem load` command supports the following argument:
|
||||||
|
* `-e` little endian parse e.g. `0xDEADBEFF -> 0xFFBEADDE`
|
2
samples/subsys/shell/devmem_load/prj.conf
Normal file
2
samples/subsys/shell/devmem_load/prj.conf
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
CONFIG_BOOT_BANNER=n
|
||||||
|
CONFIG_SHELL=y
|
4
samples/subsys/shell/devmem_load/prj_poll.conf
Normal file
4
samples/subsys/shell/devmem_load/prj_poll.conf
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
CONFIG_SHELL=y
|
||||||
|
CONFIG_SHELL_BACKEND_SERIAL_INTERRUPT_DRIVEN=n
|
||||||
|
CONFIG_SHELL_BACKEND_SERIAL_RX_POLL_PERIOD=1
|
||||||
|
CONFIG_BOOT_BANNER=n
|
10
samples/subsys/shell/devmem_load/src/main.c
Normal file
10
samples/subsys/shell/devmem_load/src/main.c
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 Antmicro <www.antmicro.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
};
|
|
@ -1,12 +1,137 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Intel Corporation
|
* Copyright (c) 2020 Intel Corporation
|
||||||
|
* Copyright (c) 2021 Antmicro <www.antmicro.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
#include <shell/shell.h>
|
#include <shell/shell.h>
|
||||||
#include <stdlib.h>
|
#include <sys/byteorder.h>
|
||||||
|
|
||||||
|
static inline bool is_ascii(uint8_t data)
|
||||||
|
{
|
||||||
|
return (data >= 0x30 && data <= 0x39) || (data >= 0x61 && data <= 0x66) ||
|
||||||
|
(data >= 0x41 && data <= 0x46);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *bytes;
|
||||||
|
static uint32_t *data;
|
||||||
|
static int sum;
|
||||||
|
static int chunk_element;
|
||||||
|
static char chunk[2];
|
||||||
|
static bool littleendian;
|
||||||
|
|
||||||
|
#define CHAR_CAN 0x18
|
||||||
|
#define CHAR_DC1 0x11
|
||||||
|
|
||||||
|
static int set_bypass(const struct shell *sh, shell_bypass_cb_t bypass)
|
||||||
|
{
|
||||||
|
static bool in_use;
|
||||||
|
|
||||||
|
if (bypass && in_use) {
|
||||||
|
shell_error(sh, "devmem load supports setting bypass on a single instance.");
|
||||||
|
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
in_use = !in_use;
|
||||||
|
if (in_use) {
|
||||||
|
shell_print(sh, "Loading...\npress ctrl-x ctrl-q to escape");
|
||||||
|
in_use = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_set_bypass(sh, bypass);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bypass_cb(const struct shell *sh, uint8_t *recv, size_t len)
|
||||||
|
{
|
||||||
|
bool escape = false;
|
||||||
|
static uint8_t tail;
|
||||||
|
uint8_t byte;
|
||||||
|
|
||||||
|
if (tail == CHAR_CAN && recv[0] == CHAR_DC1) {
|
||||||
|
escape = true;
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < (len - 1); i++) {
|
||||||
|
if (recv[i] == CHAR_CAN && recv[i + 1] == CHAR_DC1) {
|
||||||
|
escape = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (escape) {
|
||||||
|
shell_print(sh, "Number of bytes read: %d", sum);
|
||||||
|
set_bypass(sh, NULL);
|
||||||
|
|
||||||
|
if (!littleendian) {
|
||||||
|
while (sum > 4) {
|
||||||
|
*data = __bswap_32(*data);
|
||||||
|
data++;
|
||||||
|
sum = sum - 4;
|
||||||
|
}
|
||||||
|
if (sum % 4 == 0) {
|
||||||
|
*data = __bswap_32(*data);
|
||||||
|
} else if (sum % 4 == 2) {
|
||||||
|
*data = __bswap_16(*data);
|
||||||
|
} else if (sum % 4 == 3) {
|
||||||
|
*data = __bswap_24(*data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tail = recv[len - 1];
|
||||||
|
|
||||||
|
if (is_ascii(*recv)) {
|
||||||
|
chunk[chunk_element] = *recv;
|
||||||
|
chunk_element++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk_element == 2) {
|
||||||
|
byte = (uint8_t)strtoul(chunk, NULL, 16);
|
||||||
|
*bytes = byte;
|
||||||
|
bytes++;
|
||||||
|
sum++;
|
||||||
|
chunk_element = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_load(const struct shell *sh, size_t argc, char **argv)
|
||||||
|
{
|
||||||
|
littleendian = false;
|
||||||
|
char *arg;
|
||||||
|
|
||||||
|
chunk_element = 0;
|
||||||
|
sum = 0;
|
||||||
|
|
||||||
|
while (argc >= 2) {
|
||||||
|
arg = argv[1] + (!strncmp(argv[1], "--", 2) && argv[1][2]);
|
||||||
|
if (!strncmp(arg, "-e", 2)) {
|
||||||
|
littleendian = true;
|
||||||
|
} else if (!strcmp(arg, "--")) {
|
||||||
|
argv++;
|
||||||
|
argc--;
|
||||||
|
break;
|
||||||
|
} else if (arg[0] == '-' && arg[1]) {
|
||||||
|
shell_print(sh, "Unknown option \"%s\"", arg);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
argv++;
|
||||||
|
argc--;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = (unsigned char *)strtol(argv[1], NULL, 0);
|
||||||
|
data = (uint32_t *)strtol(argv[1], NULL, 0);
|
||||||
|
|
||||||
|
set_bypass(sh, bypass_cb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int memory_read(const struct shell *sh, mem_addr_t addr, uint8_t width)
|
static int memory_read(const struct shell *sh, mem_addr_t addr, uint8_t width)
|
||||||
{
|
{
|
||||||
|
@ -36,8 +161,7 @@ static int memory_read(const struct shell *sh, mem_addr_t addr, uint8_t width)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int memory_write(const struct shell *sh, mem_addr_t addr,
|
static int memory_write(const struct shell *sh, mem_addr_t addr, uint8_t width, uint64_t value)
|
||||||
uint8_t width, uint64_t value)
|
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
@ -104,5 +228,16 @@ static int cmd_devmem(const struct shell *sh, size_t argc, char **argv)
|
||||||
return memory_write(sh, addr, width, value);
|
return memory_write(sh, addr, width, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
SHELL_CMD_REGISTER(devmem, NULL, "Read/write physical memory\""
|
SHELL_STATIC_SUBCMD_SET_CREATE(sub_devmem,
|
||||||
"devmem address [width [value]]", cmd_devmem);
|
SHELL_CMD_ARG(load, NULL,
|
||||||
|
"Usage:\n"
|
||||||
|
"devmem load [options] [address]\n"
|
||||||
|
"Options:\n"
|
||||||
|
"-e\tlittle-endian parse",
|
||||||
|
cmd_load, 2, 1),
|
||||||
|
SHELL_SUBCMD_SET_END);
|
||||||
|
|
||||||
|
SHELL_CMD_REGISTER(devmem, &sub_devmem,
|
||||||
|
"Read/write physical memory\""
|
||||||
|
"devmem address [width [value]]",
|
||||||
|
cmd_devmem);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue