2 * Copyright (c) 2005 Jakub Jermar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @addtogroup genericconsole
35 * @brief Kernel console.
37 * This file contains kernel thread managing the kernel console.
41 #include <console/kconsole.h>
42 #include <console/console.h>
43 #include <console/chardev.h>
44 #include <console/cmd.h>
55 #include <sysinfo/sysinfo.h>
56 #include <ddi/device.h>
62 /** Simple kernel console.
64 * The console is realized by kernel thread kconsole.
65 * It doesn't understand any useful command on its own,
66 * but makes it possible for other kernel subsystems to
67 * register their own commands.
72 * There is a list of cmd_info_t structures. This list
73 * is protected by cmd_lock spinlock. Note that specially
74 * the link elements of cmd_info_t are protected by
77 * Each cmd_info_t also has its own lock, which protects
78 * all elements thereof except the link element.
80 * cmd_lock must be acquired before any cmd_info lock.
81 * When locking two cmd info structures, structure with
82 * lower address must be locked first.
85 SPINLOCK_INITIALIZE(cmd_lock
); /**< Lock protecting command list. */
86 LIST_INITIALIZE(cmd_list
); /**< Command list. */
88 static wchar_t history
[KCONSOLE_HISTORY
][MAX_CMDLINE
] = {};
89 static size_t history_pos
= 0;
91 /** Initialize kconsole data structures
93 * This is the most basic initialization, almost no
94 * other kernel subsystem is ready yet.
97 void kconsole_init(void)
102 for (i
= 0; i
< KCONSOLE_HISTORY
; i
++)
106 /** Register kconsole command.
108 * @param cmd Structure describing the command.
110 * @return False on failure, true on success.
113 bool cmd_register(cmd_info_t
*cmd
)
115 spinlock_lock(&cmd_lock
);
118 * Make sure the command is not already listed.
120 list_foreach(cmd_list
, cur
) {
121 cmd_info_t
*hlp
= list_get_instance(cur
, cmd_info_t
, link
);
124 /* The command is already there. */
125 spinlock_unlock(&cmd_lock
);
129 /* Avoid deadlock. */
131 spinlock_lock(&hlp
->lock
);
132 spinlock_lock(&cmd
->lock
);
134 spinlock_lock(&cmd
->lock
);
135 spinlock_lock(&hlp
->lock
);
138 if (str_cmp(hlp
->name
, cmd
->name
) == 0) {
139 /* The command is already there. */
140 spinlock_unlock(&hlp
->lock
);
141 spinlock_unlock(&cmd
->lock
);
142 spinlock_unlock(&cmd_lock
);
146 spinlock_unlock(&hlp
->lock
);
147 spinlock_unlock(&cmd
->lock
);
151 * Now the command can be added.
153 list_append(&cmd
->link
, &cmd_list
);
155 spinlock_unlock(&cmd_lock
);
159 /** Print count times a character */
160 NO_TRACE
static void print_cc(wchar_t ch
, size_t count
)
163 for (i
= 0; i
< count
; i
++)
167 /** Try to find a command beginning with prefix */
168 NO_TRACE
static const char *cmdtab_search_one(const char *name
,
171 size_t namelen
= str_length(name
);
173 spinlock_lock(&cmd_lock
);
175 if (*startpos
== NULL
)
176 *startpos
= cmd_list
.head
.next
;
178 for (; *startpos
!= &cmd_list
.head
; *startpos
= (*startpos
)->next
) {
179 cmd_info_t
*hlp
= list_get_instance(*startpos
, cmd_info_t
, link
);
181 const char *curname
= hlp
->name
;
182 if (str_length(curname
) < namelen
)
185 if (str_lcmp(curname
, name
, namelen
) == 0) {
186 spinlock_unlock(&cmd_lock
);
187 return (curname
+ str_lsize(curname
, namelen
));
191 spinlock_unlock(&cmd_lock
);
195 /** Command completion of the commands
197 * @param name String to match, changed to hint on exit
198 * @param size Input buffer size
200 * @return Number of found matches
203 NO_TRACE
static int cmdtab_compl(char *input
, size_t size
, indev_t
* indev
)
205 const char *name
= input
;
208 /* Maximum Match Length : Length of longest matching common substring in
209 case more than one match is found */
210 size_t max_match_len
= size
;
211 size_t max_match_len_tmp
= size
;
212 size_t input_len
= str_length(input
);
215 char *output
= malloc(MAX_CMDLINE
, 0);
217 size_t hints_to_show
= MAX_TAB_HINTS
- 1;
218 size_t total_hints_shown
= 0;
219 char continue_showing_hints
= 'y';
223 while ((hint
= cmdtab_search_one(name
, &pos
))) {
224 if ((found
== 0) || (str_length(output
) > str_length(hint
)))
225 str_cpy(output
, MAX_CMDLINE
, hint
);
231 /* If possible completions are more than MAX_TAB_HINTS, ask user whether to display them or not. */
232 if (found
> MAX_TAB_HINTS
) {
233 printf("\nDisplay all %zu possibilities? (y or n)", found
);
235 display
= indev_pop_character(indev
);
236 } while (display
!= 'y' && display
!= 'n' && display
!= 'Y' && display
!= 'N');
239 if ((found
> 1) && (str_length(output
) != 0)) {
242 while (cmdtab_search_one(name
, &pos
)) {
243 cmd_info_t
*hlp
= list_get_instance(pos
, cmd_info_t
, link
);
245 if (display
== 'y' || display
== 'Y') { /* We are still showing hints */
246 printf("%s (%s)\n", hlp
->name
, hlp
->description
);
250 if (hints_to_show
== 0 && total_hints_shown
!= found
) { /* Time to ask user to continue */
253 continue_showing_hints
= indev_pop_character(indev
);
254 if (continue_showing_hints
== 'y' || continue_showing_hints
== 'Y'
255 || continue_showing_hints
== ' ') {
256 hints_to_show
= MAX_TAB_HINTS
- 1; /* Display a full page again */
260 if (continue_showing_hints
== 'n' || continue_showing_hints
== 'N'
261 || continue_showing_hints
== 'q' || continue_showing_hints
== 'Q') {
262 display
= 'n'; /* Stop displaying hints */
266 if (continue_showing_hints
== '\n') {
267 hints_to_show
= 1; /* Show one more hint */
272 printf("\r \r"); /* Delete the --More-- option */
277 for(max_match_len_tmp
= 0; output
[max_match_len_tmp
] == hlp
->name
[input_len
+ max_match_len_tmp
]
278 && max_match_len_tmp
< max_match_len
; ++max_match_len_tmp
);
279 max_match_len
= max_match_len_tmp
;
281 /* keep only the characters common in all completions */
282 output
[max_match_len
] = 0;
286 str_cpy(input
, size
, output
);
292 NO_TRACE
static wchar_t *clever_readline(const char *prompt
, indev_t
*indev
)
294 printf("%s> ", prompt
);
297 wchar_t *current
= history
[history_pos
];
299 char *tmp
= malloc(STR_BOUNDS(MAX_CMDLINE
), 0);
302 wchar_t ch
= indev_pop_character(indev
);
315 if (wstr_remove(current
, position
- 1)) {
318 printf("%ls ", current
+ position
);
319 print_cc('\b', wstr_length(current
) - position
+ 1);
327 /* Move to the end of the word */
328 for (; (current
[position
] != 0) && (!isspace(current
[position
]));
330 putchar(current
[position
]);
335 /* Find the beginning of the word
336 and copy it to tmp */
338 for (beg
= position
- 1; (beg
> 0) && (!isspace(current
[beg
]));
341 if (isspace(current
[beg
]))
344 wstr_to_str(tmp
, position
- beg
+ 1, current
+ beg
);
348 /* Command completion */
349 found
= cmdtab_compl(tmp
, STR_BOUNDS(MAX_CMDLINE
), indev
);
351 /* Symbol completion */
352 found
= symtab_compl(tmp
, STR_BOUNDS(MAX_CMDLINE
), indev
);
358 /* We have hints, may be many. In case of more than one hint,
359 tmp will contain the common prefix. */
362 while ((ch
= str_decode(tmp
, &off
, STR_NO_LIMIT
)) != 0) {
363 if (!wstr_linsert(current
, ch
, position
+ i
, MAX_CMDLINE
))
369 /* No unique hint, list was printed */
370 printf("%s> ", prompt
);
371 printf("%ls", current
);
372 position
+= str_length(tmp
);
373 print_cc('\b', wstr_length(current
) - position
);
379 printf("%ls", current
+ position
);
380 position
+= str_length(tmp
);
381 print_cc('\b', wstr_length(current
) - position
);
383 if (position
== wstr_length(current
)) {
384 /* Insert a space after the last completed argument */
385 if (wstr_linsert(current
, ' ', position
, MAX_CMDLINE
)) {
386 printf("%ls", current
+ position
);
393 if (ch
== U_LEFT_ARROW
) {
402 if (ch
== U_RIGHT_ARROW
) {
404 if (position
< wstr_length(current
)) {
405 putchar(current
[position
]);
411 if ((ch
== U_UP_ARROW
) || (ch
== U_DOWN_ARROW
)) {
413 print_cc('\b', position
);
414 print_cc(' ', wstr_length(current
));
415 print_cc('\b', wstr_length(current
));
417 if (ch
== U_UP_ARROW
) {
419 if (history_pos
== 0)
420 history_pos
= KCONSOLE_HISTORY
- 1;
426 history_pos
= history_pos
% KCONSOLE_HISTORY
;
428 current
= history
[history_pos
];
429 printf("%ls", current
);
430 position
= wstr_length(current
);
434 if (ch
== U_HOME_ARROW
) {
436 print_cc('\b', position
);
441 if (ch
== U_END_ARROW
) {
443 printf("%ls", current
+ position
);
444 position
= wstr_length(current
);
448 if (ch
== U_DELETE
) {
450 if (position
== wstr_length(current
))
453 if (wstr_remove(current
, position
)) {
454 printf("%ls ", current
+ position
);
455 print_cc('\b', wstr_length(current
) - position
+ 1);
460 if (wstr_linsert(current
, ch
, position
, MAX_CMDLINE
)) {
461 printf("%ls", current
+ position
);
463 print_cc('\b', wstr_length(current
) - position
);
467 if (wstr_length(current
) > 0) {
469 history_pos
= history_pos
% KCONSOLE_HISTORY
;
476 bool kconsole_check_poll(void)
478 return check_poll(stdin
);
481 NO_TRACE
static bool parse_int_arg(const char *text
, size_t len
,
487 /* If we get a name, try to find it in symbol table */
488 if (text
[0] == '&') {
492 } else if (text
[0] == '*') {
498 if ((text
[0] < '0') || (text
[0] > '9')) {
499 char symname
[MAX_SYMBOL_NAME
];
500 str_ncpy(symname
, MAX_SYMBOL_NAME
, text
, len
+ 1);
503 int rc
= symtab_addr_lookup(symname
, &symaddr
);
506 printf("Symbol %s not found.\n", symname
);
509 printf("Duplicate symbol %s.\n", symname
);
510 symtab_print_search(symname
);
513 printf("No symbol information available.\n");
517 *result
= (sysarg_t
) symaddr
;
519 *result
= **((sysarg_t
**) symaddr
);
521 *result
= *((sysarg_t
*) symaddr
);
524 printf("Unknown error.\n");
528 /* It's a number - convert it */
530 int rc
= str_uint64_t(text
, NULL
, 0, true, &value
);
533 printf("Invalid number.\n");
536 printf("Integer overflow.\n");
539 *result
= (sysarg_t
) value
;
541 *result
= *((sysarg_t
*) *result
);
544 printf("Unknown error.\n");
554 * Find start and end positions of command line argument.
556 * @param cmdline Command line as read from the input device.
557 * @param size Size (in bytes) of the string.
558 * @param start On entry, 'start' contains pointer to the offset
559 * of the first unprocessed character of cmdline.
560 * On successful exit, it marks beginning of the next argument.
561 * @param end Undefined on entry. On exit, 'end' is the offset of the first
562 * character behind the next argument.
564 * @return False on failure, true on success.
567 NO_TRACE
static bool parse_argument(const char *cmdline
, size_t size
,
568 size_t *start
, size_t *end
)
570 ASSERT(start
!= NULL
);
573 bool found_start
= false;
574 size_t offset
= *start
;
575 size_t prev
= *start
;
578 while ((ch
= str_decode(cmdline
, &offset
, size
)) != 0) {
596 /** Parse command line.
598 * @param cmdline Command line as read from input device.
599 * @param size Size (in bytes) of the string.
601 * @return Structure describing the command.
604 NO_TRACE
static cmd_info_t
*parse_cmdline(const char *cmdline
, size_t size
)
608 if (!parse_argument(cmdline
, size
, &start
, &end
)) {
609 /* Command line did not contain alphanumeric word. */
612 spinlock_lock(&cmd_lock
);
614 cmd_info_t
*cmd
= NULL
;
616 list_foreach(cmd_list
, cur
) {
617 cmd_info_t
*hlp
= list_get_instance(cur
, cmd_info_t
, link
);
618 spinlock_lock(&hlp
->lock
);
620 if (str_lcmp(hlp
->name
, cmdline
+ start
,
621 max(str_length(hlp
->name
),
622 str_nlength(cmdline
+ start
, (size_t) (end
- start
)))) == 0) {
627 spinlock_unlock(&hlp
->lock
);
630 spinlock_unlock(&cmd_lock
);
633 /* Unknown command. */
634 printf("Unknown command.\n");
638 /* cmd == hlp is locked */
641 * The command line must be further analyzed and
642 * the parameters therefrom must be matched and
643 * converted to those specified in the cmd info
649 for (i
= 0; i
< cmd
->argc
; i
++) {
653 if (!parse_argument(cmdline
, size
, &start
, &end
)) {
654 if (cmd
->argv
[i
].type
== ARG_TYPE_STRING_OPTIONAL
) {
655 buf
= (char *) cmd
->argv
[i
].buffer
;
656 str_cpy(buf
, cmd
->argv
[i
].len
, "");
660 printf("Too few arguments.\n");
661 spinlock_unlock(&cmd
->lock
);
665 switch (cmd
->argv
[i
].type
) {
666 case ARG_TYPE_STRING
:
667 case ARG_TYPE_STRING_OPTIONAL
:
668 buf
= (char *) cmd
->argv
[i
].buffer
;
669 str_ncpy(buf
, cmd
->argv
[i
].len
, cmdline
+ start
,
673 if (!parse_int_arg(cmdline
+ start
, end
- start
,
674 &cmd
->argv
[i
].intval
))
678 if ((start
< end
- 1) && (cmdline
[start
] == '"')) {
679 if (cmdline
[end
- 1] == '"') {
680 buf
= (char *) cmd
->argv
[i
].buffer
;
681 str_ncpy(buf
, cmd
->argv
[i
].len
,
684 cmd
->argv
[i
].intval
= (sysarg_t
) buf
;
685 cmd
->argv
[i
].vartype
= ARG_TYPE_STRING
;
687 printf("Wrong syntax.\n");
690 } else if (parse_int_arg(cmdline
+ start
,
691 end
- start
, &cmd
->argv
[i
].intval
)) {
692 cmd
->argv
[i
].vartype
= ARG_TYPE_INT
;
694 printf("Unrecognized variable argument.\n");
698 case ARG_TYPE_INVALID
:
700 printf("Invalid argument type\n");
707 spinlock_unlock(&cmd
->lock
);
712 if (parse_argument(cmdline
, size
, &start
, &end
)) {
713 printf("Too many arguments.\n");
714 spinlock_unlock(&cmd
->lock
);
718 spinlock_unlock(&cmd
->lock
);
722 /** Kernel console prompt.
724 * @param prompt Kernel console prompt (e.g kconsole/panic).
725 * @param msg Message to display in the beginning.
726 * @param kcon Wait for keypress to show the prompt
730 void kconsole(const char *prompt
, const char *msg
, bool kcon
)
733 LOG("No stdin for kernel console");
741 indev_pop_character(stdin
);
743 printf("Type \"exit\" to leave the console.\n");
745 char *cmdline
= malloc(STR_BOUNDS(MAX_CMDLINE
), 0);
747 wchar_t *tmp
= clever_readline((char *) prompt
, stdin
);
748 size_t len
= wstr_length(tmp
);
752 wstr_to_str(cmdline
, STR_BOUNDS(MAX_CMDLINE
), tmp
);
754 if ((!kcon
) && (len
== 4) && (str_lcmp(cmdline
, "exit", 4) == 0))
757 cmd_info_t
*cmd_info
= parse_cmdline(cmdline
, STR_BOUNDS(MAX_CMDLINE
));
761 (void) cmd_info
->func(cmd_info
->argv
);
766 /** Kernel console managing thread.
769 void kconsole_thread(void *data
)
771 kconsole("kconsole", "Kernel console ready (press any key to activate)\n", true);