diff options
author | Tarek El-Sherbiny <tarek.el-sherbiny@arm.com> | 2020-01-02 17:02:26 +0000 |
---|---|---|
committer | jimqui01 <54316584+jimqui01@users.noreply.github.com> | 2020-03-23 14:22:21 +0000 |
commit | cbb7d586d315a4b53dacd5d6a0c5ee3b7d4dd30c (patch) | |
tree | e6f315820b7ef29ef666bcb82a3b8970001d555f /debugger | |
parent | 9c9dd077b78578969f0a7ec6b5b1fc3280c10c04 (diff) |
dbg: Import the CLI code
This patch imports existing CLI implementaion.
Change-Id: I65c79811e90f866980ded22dde82fbfe3c01f853
Signed-off-by: Tarek El-Sherbiny <tarek.el-sherbiny@arm.com>
Diffstat (limited to 'debugger')
-rw-r--r-- | debugger/include/cli.h | 275 | ||||
-rw-r--r-- | debugger/include/cli_config.h | 60 | ||||
-rw-r--r-- | debugger/include/cli_fifo.h | 145 | ||||
-rw-r--r-- | debugger/include/cli_platform.h | 123 | ||||
-rw-r--r-- | debugger/src/Makefile | 19 | ||||
-rw-r--r-- | debugger/src/cli/cli.c | 1442 | ||||
-rw-r--r-- | debugger/src/cli/cli_commands_checkpoint.c | 156 | ||||
-rw-r--r-- | debugger/src/cli/cli_commands_core.c | 344 | ||||
-rw-r--r-- | debugger/src/cli/cli_fifo.c | 133 | ||||
-rw-r--r-- | debugger/src/cli/readme.txt | 93 |
10 files changed, 2790 insertions, 0 deletions
diff --git a/debugger/include/cli.h b/debugger/include/cli.h new file mode 100644 index 00000000..74e89fed --- /dev/null +++ b/debugger/include/cli.h @@ -0,0 +1,275 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _CLI_H_ +#define _CLI_H_ + +/*****************************************************************************/ +/* Include Files */ +/*****************************************************************************/ + +#include <stdbool.h> +#include <stdint.h> + +#include "cli_config.h" +#include "cli_fifo.h" + +/*****************************************************************************/ +/* Enumerated Types */ +/*****************************************************************************/ + +/* + * cli_state_et + * Description + * Describes the current state of the CLI. + * Members + * CLI_NOT_READY + * CLI has not been initialized and is not running. + * CLI_READY + * Data structures have been initialized, threads can register and print, + * but print operations will be slow as they will have to use the UART + * hardware. + * CLI_RUNNING + * The CLI thread is up and running, threads can register and print, and + * print operations will be fast as they will write into a FIFO buffer + * rather than deal with the UART hardware. + */ +typedef enum { + CLI_NOT_READY = 0x0, + CLI_READY = 0x1, + CLI_RUNNING = 0x2 +} cli_state_et; + +/* Masks for formatting options. */ +#define CLI_RESET_MASK (0x00000003) +#define CLI_FORMAT_MASK (0x0000FFFC) +#define CLI_BG_COLOR_MASK (0x00FF0000) +#define CLI_TEXT_COLOR_MASK (0xFF000000) +#define CLI_ALL_MASK (CLI_FORMAT_MASK | CLI_BG_COLOR_MASK | CLI_TEXT_COLOR_MASK) +#define CLI_BG_COLOR_SHIFT (16) +#define CLI_TEXT_COLOR_SHIFT (24) + +/* + * cli_option_et + * Description + * These values are used apply special formatting to printed text in + * cli_client_printf. Options can be OR'd together to apply more than one + * at a time. You cannot use multiple background colors and multiple text + * colors together though, but a single background and a single text color + * is just fine. + * Members + * NONE + * Default terminal formatting, same as using 0. + * CLEAR_DISPLAY + * Deletes everything on the display before printing. + * RESET_CURSOR + * Moves the cursor to the top left position (1,1). + * BOLD + * Makes text bold or "bright" depending on the terminal application. + * UNDERLINE + * Underlines text. + * BG_<color> + * Text background color, only one of these can be used at a time. + * TEXT_<color> + * Text color, only one of these can be used at a time. + */ +typedef enum { + NONE = 0x00000000, + CLEAR_DISPLAY = 0x00000001, + RESET_CURSOR = 0x00000002, + BOLD = 0x00000004, + UNDERLINE = 0x00000008, + BG_BLACK = 0x001E0000, + BG_RED = 0x001F0000, + BG_GREEN = 0x00200000, + BG_YELLOW = 0x00210000, + BG_BLUE = 0x00220000, + BG_MAGENTA = 0x00230000, + BG_CYAN = 0x00240000, + BG_WHITE = 0x00250000, + TEXT_BLACK = 0x1E000000, + TEXT_RED = 0x1F000000, + TEXT_GREEN = 0x20000000, + TEXT_YELLOW = 0x21000000, + TEXT_BLUE = 0x22000000, + TEXT_MAGENTA = 0x23000000, + TEXT_CYAN = 0x24000000, + TEXT_WHITE = 0x25000000 +} cli_option_et; + +/*****************************************************************************/ +/* Structure Types */ +/*****************************************************************************/ + +/*! + * \brief CLI commands structure cli_command_st. + * + * \details Structure that stores a CLI command, it includes a help string so + * help functionality is uniform across commands as well as a string used + * to define the command used to invoke the stored function pointer. + */ +typedef struct { + /*! Pointer to string that is typed to invoke a command. */ + const char *command; + /*! Pointer to string containing help information for a command. */ + const char *help; + /*! Pointer to function that is invoked for this command. */ + int32_t (*handler)(int32_t argc, char **argv); + /*! + * For complex commands that need more than a single help string, set + * this to true. This will keep the CLI from intercepting the --help and + * -h flags and will instead pass them to the command along with the rest + * of the arguments. + */ + bool ignore_help_flag; +} cli_command_st; + +/*****************************************************************************/ +/* Public Function Prototypes */ +/*****************************************************************************/ + +/* + * cli_init + * Description + * Public function used to initialize the CLI data structures but not start + * the CLI main thread. This allows other threads to register with the CLI + * before the CLI idle thread has actually been created. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t cli_init(void); + +/* + * cli_start + * Description + * Public function used to create the CLI thread after it's data structures + * have been initialized. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t cli_start(void); + +/* + * cli_bprintf + * Description + * Buffered formatted print function. Prints generated here are put into a + * memory buffer and sent to the UART during CPU idle time rather than + * waiting for the UART to accept all the data. This allows debug print + * statements to be very minimally intrusive. + * Parameters + * cli_option_et + * Text formatting options, see definition of cli_option_et above. + * const char *format + * Formatted text to be printed, like printf. + * ... + * Additional arguments for formatted text, like printf. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t cli_bprintf(cli_option_et options, const char *format, ...); + +/* + * cli_bprint + * Description + * Buffered print function that just sends a string directly to the print + * buffer with no special formatting. + * Parameters + * const char *string + * Null-terminated string to be printed. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t cli_bprint(const char *format); + +/* + * cli_printf + * Description + * Prints directly to the debug console without waiting for the CLI thread + * to pick it up. Do not use this in a thread except in the case of dire + * errors or information that needs to be printed immediately. + * Paremeters + * cli_option_et + * Text formatting options, see definition of cli_option_et above. + * const char *format + * Formatted text to be printed, like printf. + * ... + * Additional arguments for formatted text, like printf. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t cli_printf(cli_option_et options, const char *format, ...); + +/* + * cli_print + * Description + * Sends a string directly to the debug terminal with no special formatting + * or extra processing. + * Parameters + * const char *string + * Null-terminated string to be printed. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t cli_print(const char *string); + +/* + * cli_format_print + * Description + * Rudimentary formatted print function that takes the place of snprintf and + * it's variants. + * Parameters + * char *s + * Buffer to build new string in. + * char *smax + * s + len(s), points to address after the last character in s. This + * keeps the function from exceeding the boundaries of the array. + * const char *fmt + * Formatted text to parse. + * ... + * Arguments for formatted text. + */ +void cli_snprintf(char *s, char *smax, const char *fmt, ...); + +/* + * cli_getline + * Description + * Retrieves input from terminal while running a CLI command. + * Parameters + * char *buffer + * Buffer used to store typed characters. + * uint32_t buffer_size + * Size of the buffer, determines how many characters you can type. + * char **argbuf + * If not NULL, the typed string will be split up into individual + * arguments. In this case, buffer will be unusable as the parser + * replaces whitespace with null characters and creates links in argbuf to + * each argument. + * uint32_t argbuf_size + * Size of the argbuf array, determines maximum number of individual + * arguments that can be entered. Ignored if argbuf is NULL. + * uint32_t cursor_position + * Tells the CLI how far advanced the cursor is on a line at the time, if + * in doubt, print a newline then set to 0. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t cli_getline( + char *buffer, + uint32_t buffer_size, + char **argbuf, + uint32_t argbuf_size, + uint32_t cursor_position); + +/* + * TBA: Replace calls to strncmp with calls to cli_strncmp. + * For some reason calls to strncmp crash for seemingly no reason on the + * FPGA, so we'll use cli_strncmp until that gets fixed or we move over to + * silicon. + */ +int32_t cli_strncmp(const char *s1, const char *s2, uint32_t limit); + +#endif /* _CLI_H_ */ diff --git a/debugger/include/cli_config.h b/debugger/include/cli_config.h new file mode 100644 index 00000000..9a5d6197 --- /dev/null +++ b/debugger/include/cli_config.h @@ -0,0 +1,60 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _CLI_CONFIG_H_ +#define _CLI_CONFIG_H_ + +#include <fwk_status.h> + +/* + * Descriptions of console configuration macros. + * + * CLI_CONFIG_COMMAND_BUF_SIZE + * The maximum number of characters that can be entered as a single command. + * + * CLI_CONFIG_MAX_NUM_ARGUMENTS + * The maximum number of individual arguments in a single command. + * + * CLI_CONFIG_HISTORY_LENGTH + * The number of past commands stored by the console. Can be accessed with + * the up/down arrow keys. + * + * CLI_CONFIG_PROMPT_BUF_SIZE + * Size of the CLI prompt text buffer. + * + * CLI_CONFIG_DEFAULT_TERM_W + * Default assumed terminal window width. + * + * CLI_CONFIG_DEFAULT_TERM_H + * Default assumed terminal window height. + * + * CLI_CONFIG_STACK_SIZE + * Number of stack bytes requested when thread is defined. + * + * CLI_CONFIG_PRINT_BUFFER_SIZE + * Size of the debug print buffer used to store characters before they can + * be sent to the UART. + * + * CLI_CONFIG_SCRATCH_BUFFER_SIZE + * Number of stack bytes used as scratch space by print statements, size of + * this buffer determines the maximum length of a single print. Threads using + * CLI print functionality must have this much extra stack space. + */ + +#define CLI_CONFIG_COMMAND_BUF_SIZE (256) +#define CLI_CONFIG_MAX_NUM_ARGUMENTS (16) +#define CLI_CONFIG_HISTORY_LENGTH (16) +#define CLI_CONFIG_PROMPT_BUF_SIZE (16) +#define CLI_CONFIG_DEFAULT_TERM_W (80) +#define CLI_CONFIG_DEFAULT_TERM_H (24) +#define CLI_CONFIG_STACK_SIZE (2048) +#define CLI_CONFIG_PRINT_BUFFER_SIZE (1024) +#define CLI_CONFIG_SCRATCH_BUFFER_SIZE (256) + +#define CLI_PROMPT "> " + +#endif /* _CLI_CONFIG_H_ */ diff --git a/debugger/include/cli_fifo.h b/debugger/include/cli_fifo.h new file mode 100644 index 00000000..d9277aea --- /dev/null +++ b/debugger/include/cli_fifo.h @@ -0,0 +1,145 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _CLI_FIFO_H_ +#define _CLI_FIFO_H_ + +/*****************************************************************************/ +/* Structure Types */ +/*****************************************************************************/ + +typedef struct { + uint32_t put_ptr; + uint32_t get_ptr; + uint32_t count; + uint32_t high_water; + bool reset_high_water; + uint32_t buf_size; + char *buf; +} fifo_st; + +/*****************************************************************************/ +/* Function Prototypes */ +/*****************************************************************************/ + +/* + * fifo_init + * Description + * Initializes a FIFO structure, the caller must supply the buffer memory. + * Parameters + * fifo_st *fifo + * Pointer to a pre-allocated fifo_st structure. + * char *buf + * Pointer to memory to store FIFO entries. + * uint32_t buf_size + * Size of the buffer in bytes. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t fifo_init(fifo_st *fifo, char *buf, uint32_t buf_size); + +/* + * fifo_get + * Description + * Gets a byte from a FIFO and places into the address supplied. + * Parameters + * fifo_st *fifo + * Pointer to fifo_st structure to get the byte from. + * char *val + * Pointer to location in which to place the retrieved byte. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t fifo_get(fifo_st *fifo, char *val); + +/* + * fifo_put + * Description + * Places a byte into a FIFO buffer. + * Parameters + * fifo_st *fifo + * Pointer to fifo_st structure to put the byte into. + * char *val + * Pointer to location from which to get the byte. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t fifo_put(fifo_st *fifo, char *val); + +/* + * fifo_free_space + * Description + * Returns the number of unused entries in the FIFO buffer. + * Parameters + * fifo_st *fifo + * Pointer to fifo_st structure to get the free space from. + * Return + * Number of free spaces in the FIFO. + */ +uint32_t fifo_free_space(fifo_st *fifo); + +/* + * fifo_count + * Description + * Returns the number of bytes currently in a FIFO. + * Paremeters + * fifo_st *fifo + * Buffer from which to get the count. + * Return + * Number of bytes in the FIFO. + */ +uint32_t fifo_count(fifo_st *fifo); + +/* + * fifo_capacity + * Description + * Gets the maximum number of bytes that can be stored by a FIFO. + * Parameters + * fifo_st *fifo + * Pointer to FIFO structure from which to get the capacity. + * Return + * Total capacity of the FIFO, this will be one less than the buffer size. + */ +uint32_t fifo_capacity(fifo_st *fifo); + +/* + * fifo_high_water + * Description + * Returns the high-water point from the FIFO buffer. + * Parameters + * fifo_st *fifo + * Buffer structure to get the high-water count from. + * Return + * High water mark from FIFO. + */ +uint32_t fifo_high_water(fifo_st *fifo); + +/* + * fifo_high_water_reset + * Description + * Resets the high water counter by freezing it until the buffer is empty. + * Paremeters + * fifo_st *fifo + * Buffer structure to reset the high-water count in. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t fifo_high_water_reset(fifo_st *fifo); + +/* + * fifo_empty + * Description + * Removes every item from the FIFO buffer. + * Parameters + * fifo_st *fifo + * Pointer to fifo_st structure to empty. + * Return + * Platform return codes defined in cli_config.h. + */ +uint32_t fifo_empty(fifo_st *fifo); + +#endif /* _CLI_FIFO_H_ */ diff --git a/debugger/include/cli_platform.h b/debugger/include/cli_platform.h new file mode 100644 index 00000000..f9ae03f7 --- /dev/null +++ b/debugger/include/cli_platform.h @@ -0,0 +1,123 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _CLI_PLATFORM_H_ +#define _CLI_PLATFORM_H_ + +#include <stdbool.h> +#include <stdint.h> + +/* + * cli_timestamp_t + * Description + * Stores timestamp information in human-readable format. + * Members + * uint32_t days + * Number of days elapsed since restart. + * uint8_t hours + * Hours portion of time since restart. + * uint8_t minutes + * Minutes portion of time since restart. + * uint8_t seconds + * Seconds portion of time since restart. + * uint8_t fraction + * Value from 0-99 containing hundredths of seconds. + */ +typedef struct { + uint32_t days; + uint8_t hours; + uint8_t minutes; + uint8_t seconds; + uint8_t fraction; +} cli_timestamp_t; + +/*****************************************************************************/ +/* CLI Platform-Specific Function Prototypes */ +/*****************************************************************************/ + +/* + * cli_platform_get_time + * Description + * Fills out a cli_timestamp_t structure, this can be real time or time + * since system startup. + * Parameters + * cli_timestamp_t *t + * Pointer to structure to fill out. + */ +void cli_platform_get_time(cli_timestamp_t *t); + +/* + * cli_platform_delay_ms + * Descriptions + * Delays execution for a number of milliseconds. + * Parameters + * uint32_t ms + * Number of milliseconds to delay. + */ +void cli_platform_delay_ms(uint32_t ms); + +/*****************************************************************************/ +/* CLI Platform-Specific UART Functions */ +/*****************************************************************************/ + +/* + * cli_platform_uart_init + * Description + * Initializes the necessary hardware to send and receive characters. + * Return + * cli_ret_et: success if it works, something else if it fails. + */ +uint32_t cli_platform_uart_init(void); + +/* + * cli_platform_uid_notify + * Description + * If system has a UID light, this function notifies it of activity on + * the console. + * Return + * cli_ret_et: success if it works, something else if it fails. + */ +uint32_t cli_platform_uid_notify(void); + +/* + * cli_platform_uart_get + * Description + * Receives a single character from the UART. Must support blocking and + * non-blocking receive operations. + * Parameters + * char *c + * Pointer to a char in which to place the received character. + * bool block + * If true, this function must not return until a character is + * received or the UART generates an error. If false, this function + * returns immediately regardless of whether or not a character was + * received. + * Return + * cli_ret_et: success if a character is read with no errors, error_empty + * if block==true and no characters are available, or some other error + * from the UART. + */ +uint32_t cli_platform_uart_get(char *c, bool block); + +/* + * cli_platform_uart_put + * Description + * Sends a single character on the UART. This function is blocking. + * Parameters + * char *c + * Pointer to character to send. + * bool block + * If true, this function must not return until a character is + * output or the UART generates an error. If false, this function + * returns immediately regardless of whether or not a character was + * output. + * Return + * cli_ret_et: success if it works, some other error if it doesn't. + */ +uint32_t cli_platform_uart_put(const char *c, bool block); + +#endif /* _CLI_PLATFORM_H_ */ diff --git a/debugger/src/Makefile b/debugger/src/Makefile new file mode 100644 index 00000000..8a4fb2d4 --- /dev/null +++ b/debugger/src/Makefile @@ -0,0 +1,19 @@ +# +# Arm SCP/MCP Software +# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := debugger + +BS_LIB_SOURCES += \ + cli/cli.c \ + cli/cli_commands_core.c \ + cli/cli_fifo.c + +BS_LIB_INCLUDES += $(DBG_DIR)/include +BS_LIB_INCLUDES += $(ARCH_DIR)/include +BS_LIB_INCLUDES += $(FWK_DIR)/include + +include $(BS_DIR)/lib.mk diff --git a/debugger/src/cli/cli.c b/debugger/src/cli/cli.c new file mode 100644 index 00000000..a35fc098 --- /dev/null +++ b/debugger/src/cli/cli.c @@ -0,0 +1,1442 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <cli_platform.h> +#include <cli.h> +#include <cli_config.h> +#include <cli_fifo.h> + +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +/* String holding console prompt. */ +static char cli_prompt[CLI_CONFIG_PROMPT_BUF_SIZE] = { 0 }; +static char cli_prompt_size = 0; + +/* Buffer to store a command as it is typed. */ +static char cli_input_buffer[CLI_CONFIG_COMMAND_BUF_SIZE] = { 0 }; + +/* Array holding pointers to arguments after a command is parsed. */ +static char *cli_args[CLI_CONFIG_MAX_NUM_ARGUMENTS] = { 0 }; + +/* Buffer used to store the command history. */ +static char *cli_history[CLI_CONFIG_HISTORY_LENGTH] = { 0 }; +static char cli_history_buffer + [CLI_CONFIG_HISTORY_LENGTH * CLI_CONFIG_COMMAND_BUF_SIZE] = { 0 }; + +/* Default terminal size variables, these are updated at each enter press. */ +static uint32_t cli_terminal_width = CLI_CONFIG_DEFAULT_TERM_W; +static uint32_t cli_terminal_height = CLI_CONFIG_DEFAULT_TERM_H; + +/* Current state of the CLI. */ +volatile cli_state_et cli_state = CLI_NOT_READY; + +/* Extern link to buffer holding installed CLI commands. In cli_commands.h */ +extern cli_command_st cli_commands[]; + +/* Print buffer structures. */ +static fifo_st cli_print_fifo = { 0 }; +static char cli_print_fifo_buffer[CLI_CONFIG_PRINT_BUFFER_SIZE] = { 0 }; +static bool overflow = false; + +size_t strnlen(s, maxlen) register const char *s; +size_t maxlen; +{ + register const char *e; + size_t n; + + for (e = s, n = 0; *e && n < maxlen; e++, n++) + ; + return n; +} + +/* + * TBA: Replace calls to strncmp with calls to cli_strncmp. + * For some reason calls to strncmp crash for seemingly no reason on the + * FPGA, so we'll use cli_strncmp until that gets fixed or we move over to + * silicon. + */ +int32_t cli_strncmp(const char *s1, const char *s2, uint32_t limit) +{ + for (uint32_t i = 0; i < limit; i++) { + if (s1[i] != s2[i]) + /* + * If first string greater than second, return positive, if second + * string greater than first, return negative. + */ + return s1[i] - s2[i]; + else if (s1[i] == 0) + /* + * We know characters are equal, so if one of them is equal to zero + * both are, so return zero to indicate a match. + */ + return 0; + } + + /* Limit reached, return 0 as first string and second string match. */ + /* up to the required character limit. */ + return 0; +} + +/*****************************************************************************/ +/* Private function prototypes. */ +/*****************************************************************************/ + +/* + * cli_main + * Description + * Main loop for CLI thread. Handles receiving and dispatching commands. + * Parameters + * void const *argument + * Argument passed at initialization, not used. + */ +static void cli_main(void const *argument); + +/* + * cli_format + * Description + * Sends formatting escape sequences to the terminal or writes them into a + * string depending on how it's used. + * Parameters + * cli_option_et options + * Formatting options to use. + * char *buffer + * If this value is not null, escape sequences are placed into this + * buffer instead of being sent to the terminal immediately. + * Return + * uint32_t: FWK_SUCCESS if it works, something else if it + * doesn't. + */ +static uint32_t cli_format(cli_option_et options, char *buffer, uint32_t size); + +/* + * cli_get_command + * Description + * Function called in cli_main that receives bytes from the UART, + * echos them back, and deals with backspace, escape sequences, etc. + * It returns the received command when the enter key is pressed. + * Parameters + * char *buffer + * Buffer in which the received command is placed. + * uint32_t buffer_size + * Total size, in bytes, of the command buffer. + * uint32_t *command_length + * The length of the command from the terminal is placed in this pointer. + * char history[][] + * Pointer to command history buffer. + * uint32_t history_index + * Index of the most recent item in the history buffer. + * uint32_t history_size + * Size of command history buffer. + * Return + * uint32_t: FWK_SUCCESS if it works, something else if it + * doesn't. + */ +static uint32_t cli_get_command( + char *buffer, + uint32_t buffer_size, + uint32_t *command_length, + char **history, + uint32_t history_index, + uint32_t history_size); + +/* + * cli_backspace + * Description + * Handles backspace characters from keyboard and makes sure lines wrap + * properly. + * Parameters + * uint32_t *cursor + * Pointer to cursor position variable. + * uint32_t width + * Width of terminal window. + * Return + * uint32_t: FWK_SUCCESS if it works, something else if it + * doesn't. + */ +static uint32_t cli_backspace(uint32_t *cursor, uint32_t width); + +/* + * cli_split + * Description + * Takes the command buffer, replaces whitespace characters with null + * characters, and builds an array of pointers to each individual argument. + * Arguments surrounded by parenthesis are taken verbatim, others are split + * up by whitespace. + * Parameters + * char **argbuf + * Buffer in which pointers to individual arguments are placed. Null + * terminated. + * uint32_t argbuf_size + * Number of entries in argbuf, can be configured with the + * MAX_NUM_ARGUMENTS macro above. + * char *command + * Command string to be parsed. + * uint32_t command_length + * Length of the command. + * char *whitespace + * Null-terminated string of characters to consider whitespace. + * Return + * uint32_t: FWK_SUCCESS if it works, something else if it + * doesn't. + */ +static uint32_t cli_split( + char **argbuf, + uint32_t argbuf_size, + char *command, + uint32_t command_length, + const char *whitespace); + +/* + * cli_command_dispatch + * Description + * Takes the argument list provided by cli_split and determines which + * command to execute. + * Parameters + * char **args + * Pointer to an array of strings containing the arguments received from + * the terminal. + * cli_command_st *cmd + * Pointer to the array containing command structures. + * Return + * uint32_t: FWK_SUCCESS if it works, something else if it + * doesn't. + */ +static uint32_t cli_command_dispatch(char **args, cli_command_st *cmd); + +/* + * cli_debug_output + * Description + * Handles the debug printout mode of the CLI. It runs until a Ctrl+C + * press is received. + * Return + * uint32_t: FWK_SUCCESS if it works, something else if it + * doesn't. + */ +static uint32_t cli_debug_output(void); + +/* + * cli_error_handler + * Description + * Prints information about cli_ret_et return values. + * Parameters + * cli_ret_et status + * Error code to print information about. + */ +static void cli_error_handler(uint32_t status); + +/* + * cli_val2str + * Description + * Converts a number variable to a string representation of the value. + * Parameters + * char **outstr + * Pointer to string in which to put the result. + * char *smax + * Pointer to the address after the last byte in outstr, prevents + * a buffer overflow. + * uint32_t value + * Value to convert to a string. + * uint32_t base + * Base of the result, usually base 2, 10, or 16. + * int32_t fill + * How many characters you want the result to take up. + */ +static void cli_val2str( + char **outstr, + char *smax, + uint32_t value, + uint32_t base, + int32_t fill); + +/* + * cli_snprintf_arg + * Description + * Same as cli_snprintf, but uses va_list arguments. + * Parameters + * char *s + * Buffer to build new string in. + * char *smax + * s + len(s), points to address after the last character in s. This + * keeps the function from exceeding the boundaries of the array. + * const char *fmt + * Formatted text to parse. + * va_list *args + * Pointer to initialized va_list structure. + */ +static void cli_snprintf_arg( + char *s, + char *smax, + const char *fmt, + va_list *args); + +volatile uint32_t automation_mode = 0; + +/*****************************************************************************/ +/* Public Function Definitions (see cli.h for descriptions) */ +/*****************************************************************************/ + +uint32_t cli_init(void) +{ + int32_t status = FWK_SUCCESS; + char *prompt = CLI_PROMPT; + + /* This function can only run when current state is CLI_NOT_READY. */ + if (cli_state != CLI_NOT_READY) + return FWK_E_STATE; + + /* Store CLI prompt text. */ + if (strlen(prompt) > (CLI_CONFIG_PROMPT_BUF_SIZE - 1)) + return FWK_E_NOMEM; + + strncpy(cli_prompt, prompt, CLI_CONFIG_PROMPT_BUF_SIZE); + cli_prompt_size = strlen(prompt); + + /* Initializing platform UART. */ + status = cli_platform_uart_init(); + if (status != FWK_SUCCESS) + return status; + + /* Initialize print buffer FIFO. */ + status = fifo_init( + &cli_print_fifo, cli_print_fifo_buffer, CLI_CONFIG_PRINT_BUFFER_SIZE); + if (status != FWK_SUCCESS) + return status; + + /* Setting state to CLI_READY. */ + cli_state = CLI_READY; + + return FWK_SUCCESS; +} + +uint32_t cli_start(void) +{ + /* This function can only run when current state is CLI_READY. */ + if (cli_state != CLI_READY) + return FWK_E_STATE; + + /* Attempt to start thread. */ + cli_main(NULL); + + return FWK_SUCCESS; +} + +uint32_t cli_bprintf(cli_option_et options, const char *format, ...) +{ + va_list arg; + uint32_t buffer_index = 0; + char scratch_buffer[CLI_CONFIG_SCRATCH_BUFFER_SIZE] = { 0 }; + + /* Check parameters. */ + if (format == NULL) + return FWK_E_PARAM; + + /* Check CLI state. */ + if (cli_state == CLI_NOT_READY) + return FWK_E_STATE; + + /* Check if we need any escape sequence formatting. */ + if (options != 0) { + cli_format(options, scratch_buffer, CLI_CONFIG_SCRATCH_BUFFER_SIZE); + buffer_index = strnlen(scratch_buffer, CLI_CONFIG_SCRATCH_BUFFER_SIZE); + } + + /* Generated formatted print string. */ + va_start(arg, format); + cli_snprintf_arg( + (scratch_buffer + buffer_index), + (scratch_buffer + CLI_CONFIG_SCRATCH_BUFFER_SIZE), + format, + &arg); + va_end(arg); + buffer_index = strnlen(scratch_buffer, CLI_CONFIG_SCRATCH_BUFFER_SIZE); + + /* Make sure to return formatting to normal after escape sequences are used. + */ + if (options != NONE) { + strncat( + scratch_buffer, + "[\0x1B0m", + (CLI_CONFIG_SCRATCH_BUFFER_SIZE - buffer_index)); + buffer_index = strnlen(scratch_buffer, CLI_CONFIG_SCRATCH_BUFFER_SIZE); + } + + /* Check to make sure we didn't fill up our scratch buffer. */ + if (buffer_index >= CLI_CONFIG_SCRATCH_BUFFER_SIZE) + return FWK_E_NOMEM; + + /* Send formatted print data to print FIFO. */ + return cli_bprint(scratch_buffer); +} + +uint32_t cli_bprint(const char *string) +{ + uint32_t i = 0; + int32_t status = FWK_SUCCESS; + + /* Check parameters. */ + if (string == NULL) + return FWK_E_PARAM; + + /* If not ready, return an error. */ + if (cli_state == CLI_NOT_READY) + return FWK_E_STATE; + + /* If ready but not running, print directly to UART. */ + if (cli_state == CLI_READY) + return cli_print(string); + + /* Put data into FIFO. */ + for (i = 0; ; i++) { + if ((string[i] == 0) && + (i >= CLI_CONFIG_SCRATCH_BUFFER_SIZE) && + (status != FWK_SUCCESS)) + break; + status = fifo_put(&cli_print_fifo, (char *)&string[i]); + } + + /* Print an error message if the print buffer is full. */ + if ((status != FWK_SUCCESS) && (overflow == false)) { + overflow = true; + cli_print("\0x1B[31mCONSOLE ERROR:\0x1B[0m Print buffer overflow.\n"); + } + + return status; +} +static char sCLIbuffer[CLI_CONFIG_SCRATCH_BUFFER_SIZE] = { 0 }; + +static const char str_format_buf_too_small[] = + "[\0x1B31mCONSOLE ERROR:\0x1B[0m CLI format print buffer too small.\n"; +uint32_t cli_printf(cli_option_et options, const char *format, ...) +{ + va_list arg; + int32_t status = FWK_SUCCESS; + + /* Checking pointers. */ + if (format == 0) + return FWK_E_PARAM; + + /* Applying style options. */ + if (options != NONE) { + status = cli_format(options, 0, 0); + if (status != FWK_SUCCESS) + return status; + } + + /* Printing formatted text. */ + va_start(arg, format); + cli_snprintf_arg( + sCLIbuffer, + sCLIbuffer + CLI_CONFIG_SCRATCH_BUFFER_SIZE, + (char *)format, + &arg); + va_end(arg); + + /* Print format string. */ + status = cli_print((const char *)sCLIbuffer); + if (status != FWK_SUCCESS) + return status; + + /* If style options were used, reset everything to default. */ + if (options != NONE) { + cli_format(NONE, 0, 0); + if (status != FWK_SUCCESS) + return status; + } + + /* Making sure we didn't try to print too much. */ + if (strnlen((char *)sCLIbuffer, CLI_CONFIG_SCRATCH_BUFFER_SIZE) >= + CLI_CONFIG_SCRATCH_BUFFER_SIZE - 1) { + status = cli_print(str_format_buf_too_small); + if (status != FWK_SUCCESS) + return status; + return FWK_E_NOMEM; + } + + return status; +} + +uint32_t cli_print(const char *string) +{ + uint32_t index = 0; + int32_t status = FWK_SUCCESS; + + for (index = 0; string[index] != 0; index++) { + status = cli_platform_uart_put(&string[index], true); + if (status != FWK_SUCCESS) + return status; + } + return status; +} + +void cli_snprintf(char *s, char *smax, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + cli_snprintf_arg(s, smax, fmt, &args); + va_end(args); + + return; +} + +uint32_t cli_getline( + char *buffer, + uint32_t buffer_size, + char **argbuf, + uint32_t argbuf_size, + uint32_t cursor_position) +{ + char c = 0; + uint32_t index = 0; + int32_t status = FWK_SUCCESS; + + /* Validate parameters. */ + if (buffer == NULL || + buffer_size == 0 || + cursor_position >= cli_terminal_width || + argbuf == NULL || + argbuf_size == 0) + return FWK_E_PARAM; + + /* Zero out buffer and argbuf. */ + memset(buffer, 0, buffer_size); + memset(argbuf, 0, argbuf_size * sizeof(char **)); + + /* Print prompt arrow. */ + status = cli_print(CLI_PROMPT); + if (status != FWK_SUCCESS) + return status; + + /* Increment cursor position since we just printed an arrow and space. */ + cursor_position = (cursor_position + 2) % cli_terminal_width; + + /* + * Loop will terminate when the user presses enter or when an error is + * generated. If your cli_platform_uart_get is thread friendly (and it + * should be if implemented correctly), this loop will have negligible + * impact on system performance. + */ + while (1) { + /* Grab a character from the UART. */ + status = cli_platform_uart_get(&c, true); + if (status != FWK_SUCCESS) + return status; + + /* + * Ignore non-printing characters except for a few we care about. + * 0x00 - 0x1F are non-printing characters. + * 0x7F - 0xFF are non-printing characters. + * Carriage return: \r, 0x0D + * Newline: \n, 0x0C + * Backspace: \b, 0x08 + * Delete: 0x7F + */ + if ((c <= 0x1F || c >= 0x7F) && c != '\r' && c != '\n' && c != '\b' && + c != 0x7F) + continue; + + + /* If backspace (0x08) or delete (0x7F) character received. */ + if (c == '\b' || c == 0x7F) { + /* Only accept backspace presses if we're not at the beginning of + * the string. */ + if (index != 0) { + status = cli_backspace(&cursor_position, cli_terminal_width); + if (status != FWK_SUCCESS) + return status; + + /* Decrement index and set old last character to null. */ + index = index - 1; + buffer[index] = 0; + } + continue; + } + + /* If newline received. */ + if (c == '\n' || c == '\r') { + /* Making sure the rest of the buffer is zero. */ + memset(&(buffer[index]), 0, buffer_size - index); + status = cli_print("\n"); + if (status != FWK_SUCCESS) + return status; + break; + } + + /* Echo received character to console. */ + status = cli_platform_uart_put(&c, true); + if (status != FWK_SUCCESS) + return status; + + cursor_position = (cursor_position + 1) % cli_terminal_width; + + /* Incrementing indices. */ + buffer[index] = c; + index = index + 1; + if (index >= buffer_size) { + /* + * Add null termination in case the user doesn't check return codes + * and tries to use the buffer. + */ + buffer[index - 1] = 0; + return FWK_E_NOMEM; + } + + /* Add new null terminator. */ + buffer[index] = 0; + } + + status = cli_split(argbuf, argbuf_size, buffer, index, " "); + if (status != FWK_SUCCESS) + return status; + + return FWK_SUCCESS; +} + +/*****************************************************************************/ +/* Private Function Definitions */ +/*****************************************************************************/ + +static void cli_main(void const *argument) +{ + int32_t status = FWK_SUCCESS; + /* Starting history buffer index. */ + uint32_t history_index = 0; + uint32_t last_history_index = CLI_CONFIG_HISTORY_LENGTH - 1; + uint32_t command_length = 0; + + /* Thread was started successfully, set state to CLI_RUNNING. */ + cli_state = CLI_RUNNING; + + /* Initialize command history buffer pointers. */ + for (uint32_t i = 0; i < CLI_CONFIG_HISTORY_LENGTH; i++) + cli_history[i] = &cli_history_buffer[i * CLI_CONFIG_COMMAND_BUF_SIZE]; + + /* Loop forever. */ + while (1) { + /* Printing prompt text. */ + cli_printf(NONE, cli_prompt); + + /* Zero out input buffer. */ + memset(cli_input_buffer, 0, CLI_CONFIG_COMMAND_BUF_SIZE); + + /* Get command from terminal UART. */ + status = cli_get_command( + cli_input_buffer, + CLI_CONFIG_COMMAND_BUF_SIZE - 1, + &command_length, + cli_history, + history_index, + CLI_CONFIG_HISTORY_LENGTH); + if (status != FWK_SUCCESS) { + cli_error_handler(status); + continue; + } + + /* Make sure command string is not empty. */ + if (cli_input_buffer[0] == 0) + continue; + + + /* Update history buffer if command is different than the last one. */ + if (cli_strncmp( + cli_input_buffer, + cli_history[last_history_index], + CLI_CONFIG_COMMAND_BUF_SIZE) != 0) { + strncpy( + cli_history[history_index], + cli_input_buffer, + CLI_CONFIG_COMMAND_BUF_SIZE); + history_index = (history_index + 1) % CLI_CONFIG_HISTORY_LENGTH; + last_history_index = + (last_history_index + 1) % CLI_CONFIG_HISTORY_LENGTH; + } + + /* Splitting up command into individual argument strings. */ + status = cli_split( + cli_args, + CLI_CONFIG_MAX_NUM_ARGUMENTS, + cli_input_buffer, + command_length, + " "); + if (status != FWK_SUCCESS) { + cli_error_handler(status); + continue; + } + + /* If the user didn't type any valid arguments, don't process it. */ + if (cli_args[0] == 0) + continue; + + /* Dispatching command for processing. */ + status = cli_command_dispatch(cli_args, cli_commands); + if (status != FWK_SUCCESS) + cli_error_handler(status); + } +} + +uint32_t cli_format(cli_option_et options, char *buffer, uint32_t size) +{ + int32_t status = FWK_SUCCESS; + static char tmp_buf[10] = { 0 }; + + if (buffer != NULL) + /* Add a null terminator before we do anything. */ + buffer[0] = '\0'; + + if (automation_mode) + return status; + + /* If no options given, send SGR default sequence to remove formatting. */ + if (options == NONE) { + if (buffer != NULL) { + if (size < 5) + return FWK_E_NOMEM; + memcpy(buffer, "\0x1B[0m", 8); + buffer = &(buffer[4]); + size = size - 4; + } else + return cli_print("\x1B[0m"); + } + /* Clear terminal window. */ + if (options & CLEAR_DISPLAY) { + if (buffer != NULL) { + if (size < 5) + return FWK_E_NOMEM; + memcpy(buffer, "\0x1B[2J", 8); + buffer = &(buffer[4]); + size = size - 4; + } else + status = cli_print("\0x1B[2J"); + + if (status != FWK_SUCCESS) + return status; + } + /* Reset cursor. */ + if (options & RESET_CURSOR) { + if (buffer != NULL) { + if (size < 7) + return FWK_E_NOMEM; + + memcpy(buffer, "\0x1B[0;0f", 10); + buffer = &(buffer[6]); + size = size - 6; + } else + status = cli_print("\0x1B[0;0f"); + + if (status != FWK_SUCCESS) + return status; + } + /* SGR settings. */ + if (options & CLI_ALL_MASK) { + if (buffer != NULL) { + if (size < 3) + return FWK_E_NOMEM; + memcpy(buffer, "\0x1B[", 6); + buffer = &(buffer[2]); + size = size - 2; + } else { + status = cli_print("\0x1B["); + if (status != FWK_SUCCESS) + return status; + } + /* Bold/bright. */ + if (options & BOLD) { + if (buffer != NULL) { + if (size < 3) + return FWK_E_NOMEM; + + memcpy(buffer, ";1", 3); + buffer = &(buffer[2]); + size = size - 2; + } else { + status = cli_print(";1"); + if (status != FWK_SUCCESS) + return status; + } + } + /* Underlining. */ + if (options & UNDERLINE) { + if (buffer != NULL) { + if (size < 3) + return FWK_E_NOMEM; + memcpy(buffer, ";4", 3); + buffer = &(buffer[2]); + size = size - 2; + } else { + status = cli_print(";4"); + if (status != FWK_SUCCESS) + return status; + } + } + /* Background color. */ + if (options & CLI_BG_COLOR_MASK) { + if (buffer != NULL) { + if (size < 4) + return FWK_E_NOMEM; + + cli_snprintf( + tmp_buf, + tmp_buf + 10, + (const char *)";%d", + (uint16_t)( + (options & CLI_BG_COLOR_MASK) >> CLI_BG_COLOR_SHIFT)); + memcpy(buffer, tmp_buf, 4); + buffer = &(buffer[3]); + size = size - 3; + } else { + status = cli_printf( + 0, + ";%d", + (uint16_t)( + (options & CLI_BG_COLOR_MASK) >> CLI_BG_COLOR_SHIFT)); + if (status != FWK_SUCCESS) + return status; + } + } + /* Foreground color. */ + if (options & CLI_TEXT_COLOR_MASK) { + if (buffer != NULL) { + if (size < 4) + return FWK_E_NOMEM; + cli_snprintf( + tmp_buf, + tmp_buf + 10, + (const char *)";%d", + (char)((options & CLI_TEXT_COLOR_MASK) >> + CLI_TEXT_COLOR_SHIFT)); + memcpy(buffer, tmp_buf, 4); + buffer = &(buffer[3]); + size = size - 3; + } else { + status = cli_printf( + 0, + ";%d", + (char)((options & CLI_TEXT_COLOR_MASK) >> + CLI_TEXT_COLOR_SHIFT)); + if (status != FWK_SUCCESS) + return status; + } + } + if (buffer != NULL) { + if (size < 2) + return FWK_E_NOMEM; + memcpy(buffer, "m", 2); + } else { + status = cli_print("m"); + if (status != FWK_SUCCESS) + return status; + } + } + + return FWK_SUCCESS; +} + +static uint32_t cli_get_command( + char *buffer, + uint32_t buffer_size, + uint32_t *command_length, + char **history, + uint32_t history_index, + uint32_t history_size) +{ + int32_t status = FWK_SUCCESS; + uint32_t index = 0; + uint32_t cursor_index = cli_prompt_size; + char c = 0; + static char escape[8] = { 0 }; + uint32_t escape_index = 0; + uint32_t history_oldest = history_index; + bool flag_escape_sequence = false; + + (void)escape; + + /* Checking parameters. */ + if (buffer == 0 || buffer_size == 0 || history == 0 || + command_length == 0) + return FWK_E_PARAM; + + if (!automation_mode) { + /* Getting terminal window size. */ + /* Saving cursor position. */ + cli_print("\0x1B[s"); + /* Moving cursor to bottom right position. */ + cli_print("\0x1B[999;999f"); + /* Requesting new cursor position. */ + cli_print("\0x1B[6n"); + /* Restoring old cursor position. */ + cli_print("\0x1B[u"); + } + + while (1) { + /* Get character from UART. */ + status = cli_platform_uart_get(&c, true); + if (status != FWK_SUCCESS) + return status; + + /* If we received a Ctrl+C press, go to debug mode. */ + if (c == '\x03') { + *command_length = 0; + return cli_debug_output(); + } + + /* Ignoring non-printing characters except for a few we care about. */ + if (c < 0x20 && c != '\r' && c != '\n' && c != '\b') { + if (c == 0x1B) + flag_escape_sequence = true; + continue; + } + + /* Dealing with escape sequences. */ + if (flag_escape_sequence == true) { + escape[escape_index] = c; + escape[escape_index + 1] = 0; + escape_index = escape_index + 1; + + /* Escape sequences end with a letter. */ + if ((c > 0x40 && c < 0x5B) || (c > 0x60 && c < 0x7B)) { + flag_escape_sequence = false; + escape_index = 0; + + /* Up arrow press. */ + if (c == 'A') { + if (((history_oldest + 1) % history_size) != + history_index) { + /* Rewind history index by 1. */ + if (history_index == 0) + history_index = history_size - 1; + else + history_index = history_index - 1; + + /* If command entry is empty then stop and restore index + * value. */ + if (history[history_index][0] == 0) { + history_index = (history_index + 1) % history_size; + } else { + /* Erasing currently entered command. */ + for (; index > 0; index--) { + status = cli_backspace( + &cursor_index, cli_terminal_width); + if (status != FWK_SUCCESS) + return status; + } + + /* Copying history command from history buffer. */ + strncpy( + buffer, history[history_index], buffer_size); + + /* Printing history command to screen. */ + while (buffer[index] != 0) { + status = cli_platform_uart_put(&buffer[index], + true); + if (status != FWK_SUCCESS) + return status; + index = index + 1; + cursor_index = + (cursor_index + 1) % cli_terminal_width; + } + } + } + } + + /* Down arrow press. */ + if (c == 'B') { + if (history_index != history_oldest) { + /* Getting index of history item to load. */ + history_index = (history_index + 1) % history_size; + + /* Erasing everything in the currently entered command. + */ + for (; index > 0; index--) { + status = cli_backspace( + &cursor_index, cli_terminal_width); + if (status != FWK_SUCCESS) + return status; + } + + /* If we're back to the current command start fresh and + * zero buffer. */ + if (history_index == history_oldest) + memset(buffer, 0, buffer_size); + else { + /* Copying history command from history buffer. */ + strncpy( + buffer, history[history_index], buffer_size); + + /* Printing history command to screen. */ + while (buffer[index] != 0) { + status = cli_platform_uart_put(&buffer[index], + true); + if (status != FWK_SUCCESS) + return status; + + index = index + 1; + cursor_index = + (cursor_index + 1) % cli_terminal_width; + } + } + } + } + + /* Handling cursor position response sequence. */ + if (c == 'R') { + uint32_t i = 0; + for (i = 0; escape[i] != '['; i++) + ; + i++; + cli_terminal_height = strtoul(&escape[i], NULL, 0); + for (; escape[i] != ';'; i++) + ; + i++; + cli_terminal_width = strtoul(&escape[i], NULL, 0); + } + } + continue; + } + + /* If backspace (0x08) or delete (0x7F) character received. */ + if (c == 0x08 || c == 0x7F) { + /* Only accept backspace presses if we're not at the beginning of + * the string. */ + if (index != 0) { + status = cli_backspace(&cursor_index, cli_terminal_width); + if (status != FWK_SUCCESS) + return status; + index = index - 1; + buffer[index] = 0; + } + continue; + } + + /* If newline received. */ + if (c == '\n' || c == '\r') { + /* Making sure the rest of the buffer is zero. */ + memset(&(buffer[index]), 0, buffer_size - index); + *command_length = index; + return cli_print("\n"); + } + + /* Printing received character to console. */ + status = cli_platform_uart_put(&c, true); + if (status != FWK_SUCCESS) + return status; + + /* Incrementing indices. */ + buffer[index] = c; + index = index + 1; + if (index >= buffer_size) + return FWK_E_NOMEM; + buffer[index] = 0; + cursor_index = (cursor_index + 1) % cli_terminal_width; + } +} + +static uint32_t cli_backspace(uint32_t *cursor, uint32_t width) +{ + uint32_t status = FWK_SUCCESS; + + /* If cursor is at the first position of a line. */ + if (*cursor == 0) { + status = cli_printf(0, "\0x1B[A\0x1B[%dC ", width - 1); + if (status != FWK_SUCCESS) + return status; + *cursor = width - 1; + } else { + /* For compatibility, print back, space, back. */ + status = cli_print("\0x1B[D \0x1B[D"); + if (status != FWK_SUCCESS) + return status; + *cursor = *cursor - 1; + } + + return FWK_SUCCESS; +} + +static uint32_t cli_split( + char **argbuf, + uint32_t argbuf_size, + char *command, + uint32_t command_length, + const char *whitespace) +{ + uint32_t index = 0; + uint32_t argbuf_ctr = 0; + bool flag_paren = false; + bool flag_last_was_whitespace = true; + + /* Checking pointers. */ + if (argbuf == 0 || command == 0 || argbuf_size == 0 || whitespace == 0) + return FWK_E_PARAM; + + argbuf[0] = 0; + + for (index = 0; index < command_length; index++) { + /* If whitespace is encountered outside of parenthesis, change it to + * null and set flag. */ + if (strchr(whitespace, command[index]) != 0 && flag_paren == false) { + command[index] = 0; + flag_last_was_whitespace = true; + continue; + } + + /* Handle parenthesis. */ + if (command[index] == '\"') { + /* If we've reached the end of an arg in parenthesis, reset flag. */ + if (flag_paren == true) { + flag_paren = false; + command[index] = 0; + flag_last_was_whitespace = true; + continue; + } + + /* If we receive an opening parenthesis preceded by a whitespace + * character, mark null and enter parenthesis processing. */ + if (flag_paren == false && flag_last_was_whitespace == true) { + flag_paren = true; + command[index] = 0; + continue; + } + } + + /* First regular character after whitespace encountered. */ + if ((strchr(whitespace, command[index]) == 0 || flag_paren == true) && + flag_last_was_whitespace == true) { + flag_last_was_whitespace = false; + if (argbuf_ctr + 1 >= argbuf_size) + return FWK_E_NOMEM; + argbuf[argbuf_ctr] = &command[index]; + argbuf[argbuf_ctr + 1] = 0; + argbuf_ctr = argbuf_ctr + 1; + } + } + + /* If the user forgot to close their parenthesis, return an error. */ + if (flag_paren == true) + return FWK_E_PARAM; + + return FWK_SUCCESS; +} + +static uint32_t cli_command_dispatch(char **args, cli_command_st *cmd) +{ + uint32_t cmd_index = 0; + uint32_t index = 0; + uint32_t num_args = 0; + uint32_t status = FWK_SUCCESS; + + /* Checking pointers. */ + if (args == 0 || cmd == 0) + return FWK_E_PARAM; + + /* Special case command: help. */ + if (cli_strncmp(args[0], "help", 5) == 0) { + for (index = 0; cli_commands[index].command != 0; index++) { + cli_printf(NONE, "%s\n", cli_commands[index].command); + cli_print(cli_commands[index].help); + cli_print("\n"); + } + cli_printf(NONE, "help\n"); + cli_print(" Displays this information.\n"); + cli_printf(NONE, "Ctrl+C\n"); + cli_print(" Displays debug output from running threads.\n"); + return FWK_SUCCESS; + } + + if (cli_strncmp(args[0], "AUTO", 4) == 0) { + cli_printf(NONE, "AUTO Mode ON\n"); + automation_mode = 1; + return FWK_SUCCESS; + } + + if (cli_strncmp(args[0], "OTUA", 4) == 0) { + cli_printf(NONE, "AUTO Mode OFF\n"); + automation_mode = 0; + return FWK_SUCCESS; + } + + /* Searching for command handler. */ + /* Using strcmp here because each entry in args is guaranteed to be null + * terminated by cli_split. */ + for (cmd_index = 0; cmd[cmd_index].command != 0 && + strcmp(args[0], cmd[cmd_index].command) != 0; + cmd_index++) + ; + if (cmd[cmd_index].command == 0) + return FWK_E_SUPPORT; + + + /* Handler found, if -h or --help is given just print it's help string and + * return. */ + if (cmd[cmd_index].ignore_help_flag == false) { + for (index = 1; + (args[index] != 0) && (cli_strncmp(args[index], "-h", 3) != 0) && + (cli_strncmp(args[index], "--help", 7) != 0); + index++) + ; + if ((args[index] != 0 || cmd[cmd_index].handler == 0) && + cmd[cmd_index].help != 0) { + status = cli_print(cmd[cmd_index].help); + if (status != FWK_SUCCESS) + return status; + + /* Print a newline since help strings shouldn't have newlines at the + * end. */ + cli_print("\n"); + return status; + } + } + + /* Counting arguments. */ + for (num_args = 0; args[num_args] != 0; num_args++) + ; + + /* Calling command handler. */ + /* args is incremented so the command just gets the arguments and not the + * name of itself. */ + if (cmd[cmd_index].handler != 0) + return cmd[cmd_index].handler(num_args, args); + else + return FWK_E_PARAM; +} + +static uint32_t cli_debug_output(void) +{ + char c = 0; + uint32_t fifo_status = FWK_SUCCESS; + + cli_printf( + 0, + "\nNow showing debug console output, to return to the command line, " + "press Ctrl+C.\n"); + while (1) { + /* Looking for Ctrl+C press. */ + if (cli_platform_uart_get((char *)&c, false) == FWK_SUCCESS) { + if (c == '\x03') { + cli_printf( + 0, + "\nNow showing command line, to return to debug output, " + "press Ctrl+C.\n"); + return FWK_SUCCESS; + } + } + + /* Read from print FIFO. */ + fifo_status = fifo_get(&cli_print_fifo, &c); + if (fifo_status == FWK_SUCCESS) { + overflow = false; + cli_platform_uart_put((char *)&c, true); + } else + /* If no characters are available, let other stuff run. */ + cli_platform_delay_ms(0); + } +} + +static void cli_error_handler(uint32_t status) +{ + if (status == FWK_SUCCESS) + return; + + cli_printf(NONE, "CONSOLE ERROR: "); + switch (status) { + case FWK_E_NOMEM: + cli_print("Buffer size.\n"); + break; + case FWK_E_PARAM: + cli_print("Bad argument.\n"); + break; + case FWK_E_SUPPORT: + cli_print("Not found.\n"); + break; + case FWK_E_DATA: + cli_print("No data available.\n"); + break; + default: + cli_print("Unknown error.\n"); + return; + } +} + +static void cli_val2str( + char **outstr, + char *smax, + uint32_t value, + uint32_t base, + int32_t fill) +{ + /* Just need enough space to store 64 bit decimal integer */ + unsigned char str[20] = { 0 }; + int i = 0; + + do { + str[i++] = "0123456789ABCDEF"[value % base]; + } while (value /= base); + + while (--fill >= i) { + **outstr = '0'; + *outstr = *outstr + 1; + if (*outstr >= smax) + return; + } + + while (--i >= 0) { + **outstr = str[i]; + *outstr = *outstr + 1; + if (*outstr >= smax) + return; + } +} + +static void cli_snprintf_arg( + char *s, + char *smax, + const char *fmt, + va_list *args) +{ + int bit64 = 0; + int64_t num = 0; + uint64_t unum = 0; + char *str = NULL; + int fill = 0; + uint32_t most_significant = 0; + char c = 0; + + while (*fmt) { + if (*fmt == '%') { + fmt++; + bit64 = 0; + fill = 0; + /* Check the format specifier */ +loop: + switch (*fmt) { + case 'c': + c = (char)va_arg(*args, int32_t); + *s++ = c; + if (s >= smax) { + s[-1] = 0; + return; + } + break; + case 'i': /* Specifiers i and d do the same thing. */ + case 'd': + if (bit64) { + *s = 0; + return; + } + + num = va_arg(*args, int32_t); + + if (num < 0) { + *s++ = '-'; + if (s >= smax) { + s[-1] = 0; + return; + } + unum = (unsigned long int)-num; + } else { + unum = (unsigned long int)num; + } + + cli_val2str(&s, smax, unum, 10, fill); + if (s >= smax) { + s[-1] = 0; + return; + } + break; + case 's': + str = va_arg(*args, char *); + while (*str) { + *s++ = *str++; + if (s >= smax) { + s[-1] = 0; + return; + } + } + break; + case 'x': /* All hex prints use uppercase hex digits. */ + case 'X': + if (bit64) { + unum = va_arg(*args, uint64_t); + most_significant = (uint32_t)(unum >> 32); + if (most_significant) { + cli_val2str( + &s, + smax, + most_significant, + 16, + (fill >= 8) ? fill - 8 : 0); + if (s >= smax) { + s[-1] = 0; + return; + } + cli_val2str(&s, smax, unum, 16, 8); + if (s >= smax) { + s[-1] = 0; + return; + } + } else { + cli_val2str(&s, smax, unum, 16, fill); + if (s >= smax) { + s[-1] = 0; + return; + } + } + } else { + unum = va_arg(*args, uint32_t); + cli_val2str(&s, smax, unum, 16, fill); + if (s >= smax) { + s[-1] = 0; + return; + } + } + break; + case 'l': + bit64 = 1; + fmt++; + goto loop; + case 'u': + if (bit64) { + *s = 0; + return; + } + + unum = va_arg(*args, uint32_t); + + cli_val2str(&s, smax, unum, 10, fill); + if (s >= smax) { + s[-1] = 0; + return; + } + break; + case '0': + fmt++; + /* Make sure we have a number for fill length. */ + if (((*fmt) < '0') || ((*fmt) > '9')) { + *s = 0; + return; + } + fill = strtoul((char *)fmt, (char **)&fmt, 0); + goto loop; + default: + /* Exit on any other format specifier */ + *s = 0; + return; + } + fmt++; + continue; + } + + *s++ = *fmt++; + if (s == smax) { + s[-1] = 0; + return; + } + } + *s = 0; +} diff --git a/debugger/src/cli/cli_commands_checkpoint.c b/debugger/src/cli/cli_commands_checkpoint.c new file mode 100644 index 00000000..8345128a --- /dev/null +++ b/debugger/src/cli/cli_commands_checkpoint.c @@ -0,0 +1,156 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <checkpoint.h> + +#include <cli_platform.h> +#include <cli.h> + +#include <fwk_status.h> + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +extern struct cli_ctx cli_ctx; + +const char checkpoint_call[] = "checkpoint"; +const char checkpoint_help[] = + " Show all threads currently using checkpoints and their task IDs.\n" + " Usage: checkpoint list\n" + " Tell a paused thread to run to the next checkpoint.\n" + " Usage: checkpoint <task ID> next\n" + " Tell a paused thread to skip N future checkpoints.\n" + " Usage: checkpoint <task ID> skip N\n" + " Tell a paused thread to run to a checkpoint tag.\n" + " Usage: checkpoint <task ID> tag <tag>\n" + " Enable or disable all checkpoints in a thread.\n" + " Usage: checkpoint <task ID> <enable|disable>"; +int32_t checkpoint_f(int32_t argc, char **argv) +{ + uint32_t i = 0; + uint32_t run_cnt = 0; + uint32_t id = 0; + struct checkpoint_config *cp; + + if ((argc == 2) && (cli_strncmp(argv[1], "list", 4) == 0)) { + for (i = 0; i < CHECKPOINT_NUM; i++) { + cp = cli_ctx.cp_api->get(i); + if (cp->in_use == true) + cli_printf(NONE, "%s\n Task ID: %d\n", cp->name, i); + } + return FWK_SUCCESS; + } + + else if ((argc == 3) && (cli_strncmp(argv[2], "next", 4) == 0)) { + id = strtoul(argv[1], NULL, 0); + if (id >= CHECKPOINT_NUM) { + cli_print("Task ID out of range.\n"); + return FWK_E_PARAM; + } + cp = cli_ctx.cp_api->get(id); + if (cp->pause == false) { + cli_print("Thread is still running.\n"); + return FWK_E_STATE; + } + cp->tag[0] = 0; + cp->bypass = CHECKPOINT_ENABLED; + osThreadFlagsSet(cp->tid, CHECKPOINT_SIGNAL); + return FWK_SUCCESS; + } + + else if ((argc == 4) && (cli_strncmp(argv[2], "skip", 4) == 0)) { + id = strtoul(argv[1], NULL, 0); + if (id >= CHECKPOINT_NUM) { + cli_print("Task ID out of range.\n"); + return FWK_E_PARAM; + } + cp = cli_ctx.cp_api->get(id); + if (cp->pause == false) { + cli_print("Thread is still running.\n"); + return FWK_E_STATE; + } + run_cnt = strtoul(argv[3], NULL, 0); + cp->tag[0] = 0; + cp->bypass = run_cnt; + osThreadFlagsSet(cp->tid, CHECKPOINT_SIGNAL); + return FWK_SUCCESS; + } + + else if ((argc == 4) && (cli_strncmp(argv[2], "tag", 3) == 0)) { + id = strtoul(argv[1], NULL, 0); + if (id >= CHECKPOINT_NUM) { + cli_print("Task ID out of range.\n"); + return FWK_E_PARAM; + } + cp = cli_ctx.cp_api->get(id); + if (cp->pause == false) { + cli_print("Thread is still running.\n"); + return FWK_E_STATE; + } + strncpy(cp->tag, argv[3], CHECKPOINT_TAG_LEN); + cp->tag[CHECKPOINT_TAG_LEN - 1] = 0; + cp->bypass = CHECKPOINT_DISABLED; + osThreadFlagsSet(cp->tid, CHECKPOINT_SIGNAL); + return FWK_SUCCESS; + } + + else if ((argc == 3) && (cli_strncmp(argv[2], "enable", 6) == 0)) { + id = strtoul(argv[1], NULL, 0); + if (id >= CHECKPOINT_NUM) { + cli_print("Task ID out of range.\n"); + return FWK_E_PARAM; + } + cp = cli_ctx.cp_api->get(id); + cp->bypass = CHECKPOINT_ENABLED; + } + + else if ((argc == 3) && (cli_strncmp(argv[2], "disable", 7) == 0)) { + id = strtoul(argv[1], NULL, 0); + if (id >= CHECKPOINT_NUM) { + cli_print("Task ID out of range.\n"); + return FWK_E_PARAM; + } + cp = cli_ctx.cp_api->get(id); + if (cp->pause == false) { + cli_print("Thread is still running.\n"); + return FWK_E_STATE; + } + cp->bypass = CHECKPOINT_DISABLED; + osThreadFlagsSet(cp->tid, CHECKPOINT_SIGNAL); + return FWK_SUCCESS; + } + cli_print("CLI: Invalid command received:\n"); + for (i = 0; i < (uint32_t)argc; i++) + cli_printf(NONE, "Parameter %d is %s\n", i, argv[i]); + + return FWK_E_PARAM; +} + +void checkpoint_boot_state(uint32_t timeout_s) +{ + char c = 0; + + /* Make sure nothing is waiting in UART buffer. */ + while (cli_platform_uart_get(&c, false) == FWK_SUCCESS) + ; + + cli_print("Press any key to enable checkpoints before boot... "); + while (timeout_s != 0) { + if (cli_platform_uart_get(&c, false) == FWK_SUCCESS) { + cli_print("\nCheckpoints enabled.\n"); + cli_ctx.cp_api->enable_all(); + return; + } + cli_printf(NONE, "%c%d", 0x8, timeout_s); + osDelay(CHECKPOINT_SECOND_MS); + timeout_s = timeout_s - 1; + } + cli_printf(NONE, "%c0\n", 0x8); + + return; +} diff --git a/debugger/src/cli/cli_commands_core.c b/debugger/src/cli/cli_commands_core.c new file mode 100644 index 00000000..af198fe0 --- /dev/null +++ b/debugger/src/cli/cli_commands_core.c @@ -0,0 +1,344 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*****************************************************************************/ +/* */ +/* Adding New Commands */ +/* */ +/* Each new command must have, at minimum, 3 components: a string that is */ +/* typed to invoke it (<name>_call), a string describing how to use the */ +/* command (<name>_help), and a function that implements it (<name>_f). */ +/* */ +/* After implementing these three things, you need to go to the bottom of */ +/* this file and add them to the cli_commands array. */ +/* */ +/* <name>_call */ +/* This should be only lower case letters, no spaces/special characters. */ +/* */ +/* <name>_help */ +/* This can contain any characters you want and be as long as you want. It */ +/* should explain what the command does and describe each argument. Each */ +/* line of this string must start with 2 spaces so help printouts are */ +/* uniformly indented and easy to read. */ +/* */ +/* <name>_f */ +/* The prototype is cli_ret_et <name>_f(int32_t argc, char **argv). The */ +/* parameter argv is an array of strings containing arguments, and argc is */ +/* the number of arguments in argv, so very similar to argc and argc in a */ +/* typical C main function. The first argument is always the command name */ +/* and then following arguments are what the user typed. Each argument is */ +/* guaranteed to be a null terminated array of characters, so it is safe */ +/* use functions such as strcmp that depend on this. */ +/* */ +/* Useful APIs */ +/* cli_print and cli_printf */ +/* Non-formatted and formatted print functions used to write text to the */ +/* console, see cli_module.h for full descriptions. */ +/* cli_getline */ +/* Retrieves a line of user input from the console, see cli_module.h for */ +/* full description. */ +/* cli_platform_uart_get and cli_platform_uart_put */ +/* Direct access to the UART hardware, using these is not recommended */ +/* but shouldn't hurt anything. */ +/* cli_snprintf */ +/* Takes the place of snprintf and it's derivatives, a bit rudimentary */ +/* but has no heap dependence, see cli_module.h for full descriptions. */ +/* Most C Library Functions */ +/* Most of the C library is accessible, barring functions that depend on */ +/* having heap access. (snprintf and derivatives, sscanf and derivatives,*/ +/* etc.) */ +/* */ +/*****************************************************************************/ + +#include <cli_platform.h> +#include <cli.h> +#include <cli_config.h> +#include <cli_fifo.h> + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +/* + * dump_memory + * Reads the contents of a memory region and prints them to the terminal. + */ +static const char dump_memory_call[] = "dumpmem"; +static const char dump_memory_help[] = + " Reads raw bytes from memory and displays it on the screen.\n" + " Usage: dumpmem <base address> <number of bytes to read>\n" + " Base address and size must be on 8 byte boundaries.\n"; +#define NUM_BYTES_PER_LINE (8) +static int32_t dump_memory_f(int32_t argc, char **argv) +{ + uint8_t bytes[8] = { 0 }; + + /* + * Reads aligned to 8 byte bondaries so remove lower 3 bits of address and + * size parameters. + */ + uint32_t addr = (uint32_t)(strtoul(argv[1], 0, 0) & 0xFFFFFFF8); + uint32_t size = (uint32_t)(strtoul(argv[2], 0, 0) & 0x000003F8); + uint32_t i = 0; + uint32_t j = 0; + + /* Sanity check. */ + if (size == 0) + return FWK_E_PARAM; + + cli_printf(NONE, "Reading %d bytes from 0x%08x.\n", size, addr); + + /* Loop through all bytes. */ + for (i = 0; i < size; i = i + NUM_BYTES_PER_LINE) { + /* Read line worth of bytes from EEPROM. */ + memcpy((void *)bytes, (void *)(addr + i), NUM_BYTES_PER_LINE); + + /* Print line base address. */ + cli_printf(NONE, "0x%08x", addr + i); + + /* Print hex bytes. */ + for (j = 0; j < NUM_BYTES_PER_LINE; j++) + cli_printf(NONE, " %02x", bytes[j]); + + /* Print ASCII representation. */ + cli_print(" \""); + for (j = 0; j < NUM_BYTES_PER_LINE; j++) { + if ((bytes[j] >= 0x20) && (bytes[j] <= 0x7E)) + /* Character is printing. */ + cli_platform_uart_put((const char *)&bytes[j], true); + else + /* Character is non-printing so put a period. */ + cli_platform_uart_put(".", true); + } + cli_print("\"\n"); + } + + return FWK_SUCCESS; +} + +/* + * Cycle Memory + * Cycle the memory reads for up to 256 words and find the rate of change or + * deviations for given iteration + */ +#define MAX_CYCLE_BUFFER_SZ 256 +volatile uint32_t cycle_buffer[MAX_CYCLE_BUFFER_SZ] = { 0 }; + +static const char cycle_memory_call[] = "cyclemem"; +static const char cycle_memory_help[] = + " Cycle memory for given iterations and displays data shifts.\n" + " Usage: cyclemem <base address in hex> <num of words in decimal (max " + "256> <number of iterations to read in decimal>\n" + " Base address and size must be on 4 byte boundaries.\n"; +static int32_t cycle_memory_f(int32_t argc, char **argv) +{ + uint32_t addr = (uint32_t)(strtoul(argv[1], 0, 0) & 0xFFFFFFF8); + uint32_t size = (uint32_t)strtoul(argv[2], 0, 0); + uint32_t iterations = (uint32_t)strtoul(argv[3], 0, 0); + volatile uint32_t *tmp_address = (volatile uint32_t *)addr; + uint32_t deviation_count = 0; + uint32_t cycle_count, index = 0; + + /* Sanity check. */ + if ((size == 0) || (size > MAX_CYCLE_BUFFER_SZ)) + return FWK_E_PARAM; + + memset((void *)cycle_buffer, 0, sizeof(uint32_t) * MAX_CYCLE_BUFFER_SZ); + + cli_printf( + NONE, + "Cycle Compare Starts for 0x%08x for Length 0x%08x for iterations " + "%d.\n", + addr, + size, + iterations); + + /* Snapshot the current state */ + for (index = 0; index < size; index++) + cycle_buffer[index] = tmp_address[index]; + + /* Loop through all bytes. */ + for (cycle_count = 0; cycle_count < iterations; cycle_count++) { + for (index = 0; index < size; index++) { + if (tmp_address[index] != cycle_buffer[index]) { + cycle_buffer[index] = tmp_address[index]; + deviation_count++; + } + } + } + + cli_printf(NONE, "Cycle Compare Deviation Count %d \r\n", deviation_count); + + return FWK_SUCCESS; +} + +/* + * read_memory + * Reads a value from memory and displays it on the terminal. + */ +static const char read_memory_call[] = "readmem"; +static const char read_memory_help[] = + " Reads a value from memory.\n" + " Usage: readmem <address> <width in bytes 1|2|4|8>\n" + " WARNING: READING FROM ILLEGAL ADDRESSES CAN CRASH THE SYSTEM."; +static int32_t read_memory_f(int32_t argc, char **argv) +{ + /* Checking for the correct number of arguments. */ + if (argc != 3) + return FWK_E_PARAM; + + /* Getting address of access. */ + uintptr_t address = (uintptr_t)strtoul(argv[1], 0, 0); + + /* Getting width of access and making sure it is valid. */ + uint32_t width = strtoul(argv[2], 0, 0); + if (width != 1 && width != 2 && width != 4 && width != 8) + return FWK_E_PARAM; + + /* Switching based on width. */ + cli_print("Value: 0x"); + switch (width) { + case 1: + /* No boundary restrictions on single byte accesses. */ + cli_printf(NONE, "%02x", *((uint8_t *)address)); + break; + case 2: + if (address % 2) { + /* 16 bit accesses need to be aligned on 2 byte boundaries. */ + return FWK_E_PARAM; + } + cli_printf(NONE, "%04x", *((uint16_t *)address)); + break; + case 4: + if (address % 4) { + /* 32 bit accesses need to be aligned to 4 byte boundaries. */ + return FWK_E_PARAM; + } + cli_printf(NONE, "%08x", *((uint32_t *)address)); + break; + case 8: + if (address % 4) { + /* 64 bit accesses need to be aligned on 4 byte boundaries. */ + return FWK_E_PARAM; + } + cli_printf(NONE, "%016lx", *((uint64_t *)address)); + break; + } + + cli_print("\n"); + + return FWK_SUCCESS; +} + +/* + * write_memory + * Writes either an 8, 16, 32, or 64 bit value to a memory address. + */ +static const char write_memory_call[] = "writemem"; +static const char write_memory_help[] = + " Writes a value to memory.\n" + " Usage: writemem <base address> <width in bytes 1|2|4|8> <value to " + "write>\n"; +static int32_t write_memory_f(int32_t argc, char **argv) +{ + /* Checking for the correct number of arguments. */ + if (argc != 4) + return FWK_E_PARAM; + + /* Getting address of access. */ + uintptr_t address = (uintptr_t)strtoul(argv[1], 0, 0); + + /* Getting width of access and making sure it is valid. */ + uint32_t width = strtoul(argv[2], 0, 0); + if (width != 1 && width != 2 && width != 4 && width != 8) + return FWK_E_PARAM; + + /* Switching based on width. */ + switch (width) { + case 1: + /* No boundary restrictions on single byte accesses. */ + *((uint8_t *)address) = (uint8_t)strtoul(argv[3], 0, 0); + break; + case 2: + if (address % 2) { + /* 16 bit accesses need to be aligned on 2 byte boundaries. */ + return FWK_E_PARAM; + } + *((uint16_t *)address) = (uint16_t)strtoul(argv[3], 0, 0); + break; + case 4: + if (address % 4) { + /* 32 bit accesses need to be aligned to 4 byte boundaries. */ + return FWK_E_PARAM; + } + *((uint32_t *)address) = (uint32_t)strtoul(argv[3], 0, 0); + break; + case 8: + if (address % 4) { + /* 64 bit accesses need to be aligned on 4 byte boundaries. */ + return FWK_E_PARAM; + } + *((uint64_t *)address) = (uint64_t)strtoull(argv[3], 0, 0); + break; + } + + return FWK_SUCCESS; +} + +/* + * time + * Prints the system up time or real time. + */ +static const char uptime_call[] = "uptime"; +static const char uptime_help[] = " Prints the system uptime."; +static int32_t uptime_f(int32_t argc, char **argv) +{ + cli_timestamp_t t = { 0 }; + cli_platform_get_time(&t); + cli_printf( + NONE, + "System Uptime: %02d:%02d:%02d:%02d.%02d\n", + t.days, + t.hours, + t.minutes, + t.seconds, + t.fraction); + return FWK_SUCCESS; +} + +/* + * reset_system + * Performs a software reset. + */ +static const char reset_sys_call[] = "reset"; +static const char reset_sys_help[] = " Resets the system immediately."; +static int32_t reset_sys_f(int32_t argc, char **argv) +{ + cli_print("This command is not implemented.\n"); + return 0; +} + +/*****************************************************************************/ +/* Command Structure Array */ +/*****************************************************************************/ + +/* The last parameter in each of the commands below indicates whether the */ +/* command handles its own help or not. Right now, the PCIe/CCIX commands */ +/* are the only ones that do that. */ + +cli_command_st cli_commands[] = { + /* Add commands in this section. */ + { dump_memory_call, dump_memory_help, &dump_memory_f, false }, + { cycle_memory_call, cycle_memory_help, &cycle_memory_f, false }, + { read_memory_call, read_memory_help, &read_memory_f, false }, + { write_memory_call, write_memory_help, &write_memory_f, false }, + { reset_sys_call, reset_sys_help, &reset_sys_f, false }, + { uptime_call, uptime_help, &uptime_f, false }, + + /* End of commands. */ + { 0, 0, 0 } +}; diff --git a/debugger/src/cli/cli_fifo.c b/debugger/src/cli/cli_fifo.c new file mode 100644 index 00000000..76a8a566 --- /dev/null +++ b/debugger/src/cli/cli_fifo.c @@ -0,0 +1,133 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdbool.h> +#include <stdint.h> + +#include <cli_config.h> +#include <cli_fifo.h> + +uint32_t fifo_init(fifo_st *fifo, char *buf, uint32_t buf_size) +{ + if (fifo == 0 || buf == 0 || buf_size == 0) + return FWK_E_PARAM; + + fifo->get_ptr = 0; + fifo->put_ptr = 0; + fifo->count = 0; + fifo->high_water = 0; + fifo->reset_high_water = false; + fifo->buf_size = buf_size; + fifo->buf = buf; + return FWK_SUCCESS; +} + +uint32_t fifo_get(fifo_st *fifo, char *val) +{ + /* Checking parameters. */ + if (fifo == 0 || val == 0) + return FWK_E_PARAM; + + /* Making sure FIFO isn't empty. */ + if (fifo->get_ptr != fifo->put_ptr) { + *val = fifo->buf[fifo->get_ptr]; + fifo->get_ptr = (fifo->get_ptr + 1) % fifo->buf_size; + fifo->count = fifo->count - 1; + /* Tracking FIFO high-water mark. */ + if ((fifo->reset_high_water == true) && (fifo->count == 0)) { + fifo->high_water = 0; + fifo->reset_high_water = false; + } + return FWK_SUCCESS; + } + + return FWK_E_DATA; +} + +uint32_t fifo_put(fifo_st *fifo, char *val) +{ + /* Checking parameters. */ + if (fifo == 0 || val == 0) + return FWK_E_PARAM; + + uint32_t newPutPtr = (fifo->put_ptr + 1) % fifo->buf_size; + + /* Making sure FIFO isn't full. */ + if (newPutPtr != fifo->get_ptr) { + fifo->buf[fifo->put_ptr] = *val; + fifo->put_ptr = newPutPtr; + fifo->count = fifo->count + 1; + /* Tracking FIFO high-water mark. */ + if (fifo->count > fifo->high_water && fifo->reset_high_water == false) + fifo->high_water = fifo->count; + + return FWK_SUCCESS; + } + + return FWK_E_NOMEM; +} + +uint32_t fifo_free_space(fifo_st *fifo) +{ + /* Checking parameters. */ + if (fifo == 0) + return FWK_E_PARAM; + + return fifo->buf_size - fifo->count - 1; +} + +uint32_t fifo_count(fifo_st *fifo) +{ + /* Checking parameters. */ + if (fifo == 0) + return FWK_E_PARAM; + + return fifo->count; +} + +uint32_t fifo_capacity(fifo_st *fifo) +{ + /* Checking parameters. */ + if (fifo == 0) + return FWK_E_PARAM; + + return fifo->buf_size - 1; +} + +uint32_t fifo_high_water(fifo_st *fifo) +{ + /* Checking parameters. */ + if (fifo == 0) + return FWK_E_PARAM; + + return fifo->high_water; +} + +uint32_t fifo_high_water_reset(fifo_st *fifo) +{ + /* Checking parameters. */ + if (fifo == 0) + return FWK_E_PARAM; + + fifo->reset_high_water = true; + return FWK_SUCCESS; +} + +uint32_t fifo_empty(fifo_st *fifo) +{ + /* Check for valid pointer. */ + if (fifo == 0) + return FWK_E_PARAM; + + fifo->get_ptr = 0; + fifo->put_ptr = 0; + fifo->count = 0; + fifo->high_water = 0; + fifo->reset_high_water = false; + + return FWK_SUCCESS; +} diff --git a/debugger/src/cli/readme.txt b/debugger/src/cli/readme.txt new file mode 100644 index 00000000..6d6c520d --- /dev/null +++ b/debugger/src/cli/readme.txt @@ -0,0 +1,93 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +Description of Files + + cli_commands_core.c + Contains all code implementing core executable commands for the debug console. + If you wish to add new commands, create a new cli_commands_<name>.c file and + add links to the command functions in the command structure. + + cli_config.h + Definitions of compile-time command line interface settings, such as buffer + sizes, default prompt text, platform return codes, etc. + + cli_fifo.c + Function definitions for creating and accessing a FIFO buffer. Used to + store characters from the keyboard before they can be read by the CLI + thread as well as acting as a print buffer to store characters before + they can be sent to the UART. + + cli_fifo.h + Header file containing function prototypes and descriptions for cli_fifo.c. + + cli_platform.c + Platform-specific function definitions. Porting this CLI to almost + anything can be done entirely by editing this file. + + cli_platform.h + Function prototypes and descriptions for platform-specific functions. If + you wish to port this CLI, start by reading this file and then implement + the functions in cli_platform.c. + + cli.c + Core CLI function definitions. Contains both public and private CLI + functions. Private function prototypes and descriptions are also here. + + cli.h + CLI enumerated types, structure types, and public function prototypes and + descriptions. To access CLI functionality from other threads, you only + need to include this file. + +Using the Console + + By default the CLI is in command mode, to exit command mode and enter debug, + mode press Ctrl+C. To return to command mode, press Ctrl+C again. Know that, + while in command mode, the print buffers can fill up quickly. Once this happens, + all debug data from the time the print buffers fill up to the time you + reenter debug mode and the CLI can empty the buffers will be lost. If a + buffer fills up, you will see a message like "CONSOLE ERROR: Print buffer + overflow." + +Printing From Other Threads + + cli_printf and cli_bprintf are formatted printing functions, and cli_print and + cli_bprint are non-formatted basic printing functions. The cli_bprint + functions are buffered, meaning print data is placed into a FIFO buffer rather + than immediately put on the UART. This allows the print functions to return + very quickly rather than having to wait on the UART, but prints are delayed + until the processor has time to print them. + + Note that the maximum size of individual print text is limited by the + size of the scratch buffer (defined in cli_config.h) and how much space your + print buffer has. Text style options take up more space still. + + Also, to use this CLI to print from a thread will require enough extra stack + space to allocate the scratch buffer. + +CLI formatted print options + + This CLI is not 100% compliant with typical C formatted print strings, it is + based on a very lightweight but rudimentary version of snprintf, so some fancy + formatting options will not work as intended. (or at all) Basic specifiers + supported are listed below. + + Specifier Output Example + d/i Signed decimal integer -438 + u Unsigned decimal integer 2841 + x/X Uppercase hex value (lower case not implemented) 4AB1 + c Single character e + s Null-terminated string of characters example + l Used within a specifier, indicates 64 bit value. + 0<width> Left-pads with zeros, width is required with this. 00004AB1 + + These all work in the simplest use case, but things like right-justifying, + floating point values, and anything not fairly simple probably won't work so + make sure to test things first. + + If you want to know exactly how strings are parsed, see the function + 'cli_snprintf_arg' in cli_module.c. |