aboutsummaryrefslogtreecommitdiff
path: root/debugger
diff options
context:
space:
mode:
authorTarek El-Sherbiny <tarek.el-sherbiny@arm.com>2020-01-02 17:02:26 +0000
committerjimqui01 <54316584+jimqui01@users.noreply.github.com>2020-03-23 14:22:21 +0000
commitcbb7d586d315a4b53dacd5d6a0c5ee3b7d4dd30c (patch)
treee6f315820b7ef29ef666bcb82a3b8970001d555f /debugger
parent9c9dd077b78578969f0a7ec6b5b1fc3280c10c04 (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.h275
-rw-r--r--debugger/include/cli_config.h60
-rw-r--r--debugger/include/cli_fifo.h145
-rw-r--r--debugger/include/cli_platform.h123
-rw-r--r--debugger/src/Makefile19
-rw-r--r--debugger/src/cli/cli.c1442
-rw-r--r--debugger/src/cli/cli_commands_checkpoint.c156
-rw-r--r--debugger/src/cli/cli_commands_core.c344
-rw-r--r--debugger/src/cli/cli_fifo.c133
-rw-r--r--debugger/src/cli/readme.txt93
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.