From 83164d603033dba28a0ee014e97784ef511beb4e Mon Sep 17 00:00:00 2001 From: Minggui Cao Date: Wed, 20 Jul 2022 13:15:32 +0800 Subject: [PATCH] hv: shell: improve console to modify input easier 1. make memcpy_erms as a public API; add a new one memcpy_erms_backwards, which supports to copy data from tail to head. 2. improve to use right/left/home/end key to move cursor, and support delete/backspace key to modify current input command. Tracked-On: #7931 Signed-off-by: Minggui Cao Acked-by: Eddie Dong --- hypervisor/arch/x86/lib/memory.c | 11 ++- hypervisor/debug/shell.c | 159 +++++++++++++++++++++++++------ hypervisor/debug/shell_priv.h | 1 + hypervisor/include/lib/rtl.h | 2 + 4 files changed, 145 insertions(+), 28 deletions(-) diff --git a/hypervisor/arch/x86/lib/memory.c b/hypervisor/arch/x86/lib/memory.c index 9ff58ee32..65c1a6c14 100644 --- a/hypervisor/arch/x86/lib/memory.c +++ b/hypervisor/arch/x86/lib/memory.c @@ -24,7 +24,7 @@ void *memset(void *base, uint8_t v, size_t n) return base; } -static inline void memcpy_erms(void *d, const void *s, size_t slen) +void memcpy_erms(void *d, const void *s, size_t slen) { asm volatile ("rep; movsb" : "=&D"(d), "=&S"(s) @@ -32,6 +32,15 @@ static inline void memcpy_erms(void *d, const void *s, size_t slen) : "memory"); } +/* copy data from tail to head (backwards) with ERMS (Enhanced REP MOVSB/STOSB) */ +void memcpy_erms_backwards(void *d, const void *s, size_t slen) +{ + asm volatile ("std; rep; movsb; cld" + : "=&D"(d), "=&S"(s) + : "c"(slen), "0" (d), "1" (s) + : "memory"); +} + /* * @brief Copies at most slen bytes from src address to dest address, up to dmax. * diff --git a/hypervisor/debug/shell.c b/hypervisor/debug/shell.c index 979bd464f..ec8eb1f59 100644 --- a/hypervisor/debug/shell.c +++ b/hypervisor/debug/shell.c @@ -157,12 +157,17 @@ static struct shell_cmd shell_cmds[] = { }, }; -/* for function key: up/down key */ +/* for function key: up/down/right/left/home/end and delete key */ enum function_key { KEY_NONE, + KEY_DELETE = 0x5B33, KEY_UP = 0x5B41, KEY_DOWN = 0x5B42, + KEY_RIGHT = 0x5B43, + KEY_LEFT = 0x5B44, + KEY_END = 0x5B46, + KEY_HOME = 0x5B48, }; /* The initial log level*/ @@ -287,6 +292,40 @@ static void clear_input_line(uint32_t len) } } +static void set_cursor_pos(uint32_t left_offset) +{ + while (left_offset > 0) { + left_offset--; + shell_puts("\b"); + } +} + +static void handle_delete_key(void) +{ + if (p_shell->cursor_offset < p_shell->input_line_len) { + + uint32_t delta = p_shell->input_line_len - p_shell->cursor_offset - 1; + + /* Send a space + backspace sequence to delete character */ + shell_puts(" \b"); + + /* display the left input chars and remove former last one */ + shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset + 1); + shell_puts(" \b"); + + set_cursor_pos(delta); + + memcpy_erms(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset, + p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset + 1, delta); + + /* Null terminate the last character to erase it */ + p_shell->buffered_line[p_shell->input_line_active][p_shell->input_line_len - 1] = 0; + + /* Reduce the length of the string by one */ + p_shell->input_line_len--; + } +} + static void handle_updown_key(enum function_key key_value) { int32_t to_select, current_select = p_shell->to_select_index; @@ -316,6 +355,10 @@ static void handle_updown_key(enum function_key key_value) } if (strcmp(p_shell->buffered_line[current_select], p_shell->buffered_line[p_shell->input_line_active]) != 0) { + /* reset cursor pos and clear current input line first, then output selected cmd */ + if (p_shell->cursor_offset < p_shell->input_line_len) { + shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset); + } clear_input_line(p_shell->input_line_len); shell_puts(p_shell->buffered_line[current_select]); @@ -325,6 +368,7 @@ static void handle_updown_key(enum function_key key_value) memcpy_s(p_shell->buffered_line[p_shell->input_line_active], SHELL_CMD_MAX_LEN, p_shell->buffered_line[current_select], len + 1); p_shell->input_line_len = len; + p_shell->cursor_offset = len; } } @@ -333,15 +377,46 @@ static void shell_handle_special_char(char ch) enum function_key key_value = KEY_NONE; switch (ch) { - /* original function key value: ESC + key, so consume the next 2 characters */ + /* original function key value: ESC + key (2/3 bytes), so consume the next 2/3 characters */ case 0x1b: key_value = (shell_getc() << 8) | shell_getc(); + if (key_value == KEY_DELETE) { + (void)shell_getc(); /* delete key has one more byte */ + } switch (key_value) { + case KEY_DELETE: + handle_delete_key(); + break; case KEY_UP: case KEY_DOWN: handle_updown_key(key_value); break; + case KEY_RIGHT: + if (p_shell->cursor_offset < p_shell->input_line_len) { + shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset); + p_shell->cursor_offset++; + set_cursor_pos(p_shell->input_line_len - p_shell->cursor_offset); + } + break; + case KEY_LEFT: + if (p_shell->cursor_offset > 0) { + p_shell->cursor_offset--; + shell_puts("\b"); + } + break; + case KEY_END: + if (p_shell->cursor_offset < p_shell->input_line_len) { + shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset); + p_shell->cursor_offset = p_shell->input_line_len; + } + break; + case KEY_HOME: + if (p_shell->cursor_offset > 0) { + set_cursor_pos(p_shell->cursor_offset); + p_shell->cursor_offset = 0; + } + break; default: break; } @@ -358,6 +433,57 @@ static void shell_handle_special_char(char ch) } } +static void handle_backspace_key(void) +{ + /* Ensure length is not 0 */ + if (p_shell->cursor_offset > 0U) { + /* Echo backspace */ + shell_puts("\b"); + /* Send a space + backspace sequence to delete character */ + shell_puts(" \b"); + + if (p_shell->cursor_offset < p_shell->input_line_len) { + uint32_t delta = p_shell->input_line_len - p_shell->cursor_offset; + + /* display the left input-chars and remove the former last one */ + shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset); + shell_puts(" \b"); + + set_cursor_pos(delta); + memcpy_erms(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset - 1, + p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset, delta); + } + + /* Null terminate the last character to erase it */ + p_shell->buffered_line[p_shell->input_line_active][p_shell->input_line_len - 1] = 0; + + /* Reduce the length of the string by one */ + p_shell->input_line_len--; + p_shell->cursor_offset--; + } +} + +static void handle_input_char(char ch) +{ + uint32_t delta = p_shell->input_line_len - p_shell->cursor_offset; + + /* move the input from cursor offset back first */ + if (delta > 0) { + memcpy_erms_backwards(p_shell->buffered_line[p_shell->input_line_active] + p_shell->input_line_len, + p_shell->buffered_line[p_shell->input_line_active] + p_shell->input_line_len - 1, delta); + } + + p_shell->buffered_line[p_shell->input_line_active][p_shell->cursor_offset] = ch; + + /* Echo back the input */ + shell_puts(p_shell->buffered_line[p_shell->input_line_active] + p_shell->cursor_offset); + set_cursor_pos(delta); + + /* Move to next character in string */ + p_shell->input_line_len++; + p_shell->cursor_offset++; +} + static bool shell_input_line(void) { bool done = false; @@ -369,22 +495,7 @@ static bool shell_input_line(void) switch (ch) { /* Backspace */ case '\b': - /* Ensure length is not 0 */ - if (p_shell->input_line_len > 0U) { - /* Reduce the length of the string by one */ - p_shell->input_line_len--; - - /* Null terminate the last character to erase it */ - p_shell->buffered_line[p_shell->input_line_active][p_shell->input_line_len] = 0; - - /* Echo backspace */ - shell_puts("\b"); - - /* Send a space + backspace sequence to delete - * character - */ - shell_puts(" \b"); - } + handle_backspace_key(); break; /* Carriage-return */ @@ -397,6 +508,7 @@ static bool shell_input_line(void) /* Reset command length for next command processing */ p_shell->input_line_len = 0U; + p_shell->cursor_offset = 0U; break; /* Line feed */ @@ -410,14 +522,7 @@ static bool shell_input_line(void) if (p_shell->input_line_len < SHELL_CMD_MAX_LEN) { /* See if a "standard" prINTable ASCII character received */ if ((ch >= 32) && (ch <= 126)) { - /* Add character to string */ - p_shell->buffered_line[p_shell->input_line_active][p_shell->input_line_len] = ch; - /* Echo back the input */ - shell_puts(&p_shell->buffered_line[p_shell->input_line_active] - [p_shell->input_line_len]); - - /* Move to next character in string */ - p_shell->input_line_len++; + handle_input_char(ch); } else { /* call special character handler */ shell_handle_special_char(ch); @@ -431,7 +536,7 @@ static bool shell_input_line(void) /* Reset command length for next command processing */ p_shell->input_line_len = 0U; - + p_shell->cursor_offset = 0U; } break; } diff --git a/hypervisor/debug/shell_priv.h b/hypervisor/debug/shell_priv.h index 902802b98..45e88ed4c 100644 --- a/hypervisor/debug/shell_priv.h +++ b/hypervisor/debug/shell_priv.h @@ -36,6 +36,7 @@ struct shell { int32_t input_line_active; /* Active input line index */ int32_t to_select_index; /* used for up/down key to select former cmds */ + uint32_t cursor_offset; /* cursor offset position from left input line */ struct shell_cmd *cmds; /* cmds supported */ uint32_t cmd_count; /* Count of cmds supported */ diff --git a/hypervisor/include/lib/rtl.h b/hypervisor/include/lib/rtl.h index b989b3da1..34695c58f 100644 --- a/hypervisor/include/lib/rtl.h +++ b/hypervisor/include/lib/rtl.h @@ -42,6 +42,8 @@ char *strchr(char *s_arg, char ch); size_t strnlen_s(const char *str_arg, size_t maxlen_arg); void *memset(void *base, uint8_t v, size_t n); int32_t memcpy_s(void *d, size_t dmax, const void *s, size_t slen); +void memcpy_erms(void *d, const void *s, size_t slen); +void memcpy_erms_backwards(void *d, const void *s, size_t slen); int64_t strtol_deci(const char *nptr); uint64_t strtoul_hex(const char *nptr); char *strstr_s(const char *str1, size_t maxlen1, const char *str2, size_t maxlen2);