source: mainline/kernel/generic/src/console/kconsole.c@ 74c8da2c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 74c8da2c was 05641a9e, checked in by Jakub Jermar <jakub@…>, 17 years ago

Revive kernel notifications.

  • Property mode set to 100644
File size: 15.8 KB
Line 
1/*
2 * Copyright (c) 2005 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
16 *
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.
27 */
28
29/** @addtogroup genericconsole
30 * @{
31 */
32
33/**
34 * @file kconsole.c
35 * @brief Kernel console.
36 *
37 * This file contains kernel thread managing the kernel console.
38 */
39
40#include <console/kconsole.h>
41#include <console/console.h>
42#include <console/chardev.h>
43#include <console/cmd.h>
44#include <print.h>
45#include <panic.h>
46#include <arch/types.h>
47#include <adt/list.h>
48#include <arch.h>
49#include <macros.h>
50#include <debug.h>
51#include <func.h>
52#include <string.h>
53#include <macros.h>
54#include <sysinfo/sysinfo.h>
55#include <ddi/device.h>
56#include <symtab.h>
57#include <errno.h>
58
59/** Simple kernel console.
60 *
61 * The console is realized by kernel thread kconsole.
62 * It doesn't understand any useful command on its own,
63 * but makes it possible for other kernel subsystems to
64 * register their own commands.
65 */
66
67/** Locking.
68 *
69 * There is a list of cmd_info_t structures. This list
70 * is protected by cmd_lock spinlock. Note that specially
71 * the link elements of cmd_info_t are protected by
72 * this lock.
73 *
74 * Each cmd_info_t also has its own lock, which protects
75 * all elements thereof except the link element.
76 *
77 * cmd_lock must be acquired before any cmd_info lock.
78 * When locking two cmd info structures, structure with
79 * lower address must be locked first.
80 */
81
82SPINLOCK_INITIALIZE(cmd_lock); /**< Lock protecting command list. */
83LIST_INITIALIZE(cmd_head); /**< Command list. */
84
85static cmd_info_t *parse_cmdline(char *cmdline, size_t len);
86static bool parse_argument(char *cmdline, size_t len, index_t *start,
87 index_t *end);
88static char history[KCONSOLE_HISTORY][MAX_CMDLINE] = {};
89
90/** Initialize kconsole data structures
91 *
92 * This is the most basic initialization, almost no
93 * other kernel subsystem is ready yet.
94 *
95 */
96void kconsole_init(void)
97{
98 unsigned int i;
99
100 cmd_init();
101 for (i = 0; i < KCONSOLE_HISTORY; i++)
102 history[i][0] = '\0';
103}
104
105/** Register kconsole command.
106 *
107 * @param cmd Structure describing the command.
108 *
109 * @return 0 on failure, 1 on success.
110 */
111int cmd_register(cmd_info_t *cmd)
112{
113 link_t *cur;
114
115 spinlock_lock(&cmd_lock);
116
117 /*
118 * Make sure the command is not already listed.
119 */
120 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
121 cmd_info_t *hlp;
122
123 hlp = list_get_instance(cur, cmd_info_t, link);
124
125 if (hlp == cmd) {
126 /* The command is already there. */
127 spinlock_unlock(&cmd_lock);
128 return 0;
129 }
130
131 /* Avoid deadlock. */
132 if (hlp < cmd) {
133 spinlock_lock(&hlp->lock);
134 spinlock_lock(&cmd->lock);
135 } else {
136 spinlock_lock(&cmd->lock);
137 spinlock_lock(&hlp->lock);
138 }
139 if ((strncmp(hlp->name, cmd->name, max(strlen(cmd->name),
140 strlen(hlp->name))) == 0)) {
141 /* The command is already there. */
142 spinlock_unlock(&hlp->lock);
143 spinlock_unlock(&cmd->lock);
144 spinlock_unlock(&cmd_lock);
145 return 0;
146 }
147
148 spinlock_unlock(&hlp->lock);
149 spinlock_unlock(&cmd->lock);
150 }
151
152 /*
153 * Now the command can be added.
154 */
155 list_append(&cmd->link, &cmd_head);
156
157 spinlock_unlock(&cmd_lock);
158 return 1;
159}
160
161/** Print count times a character */
162static void rdln_print_c(char ch, int count)
163{
164 int i;
165 for (i = 0; i < count; i++)
166 putchar(ch);
167}
168
169/** Insert character to string */
170static void insert_char(char *str, char ch, int pos)
171{
172 int i;
173
174 for (i = strlen(str); i > pos; i--)
175 str[i] = str[i - 1];
176 str[pos] = ch;
177}
178
179/** Try to find a command beginning with prefix */
180static const char *cmdtab_search_one(const char *name,link_t **startpos)
181{
182 size_t namelen = strlen(name);
183 const char *curname;
184
185 spinlock_lock(&cmd_lock);
186
187 if (!*startpos)
188 *startpos = cmd_head.next;
189
190 for (; *startpos != &cmd_head; *startpos = (*startpos)->next) {
191 cmd_info_t *hlp;
192 hlp = list_get_instance(*startpos, cmd_info_t, link);
193
194 curname = hlp->name;
195 if (strlen(curname) < namelen)
196 continue;
197 if (strncmp(curname, name, namelen) == 0) {
198 spinlock_unlock(&cmd_lock);
199 return curname+namelen;
200 }
201 }
202 spinlock_unlock(&cmd_lock);
203 return NULL;
204}
205
206
207/** Command completion of the commands
208 *
209 * @param name - string to match, changed to hint on exit
210 * @return number of found matches
211 */
212static int cmdtab_compl(char *name)
213{
214 static char output[/*MAX_SYMBOL_NAME*/128 + 1];
215 link_t *startpos = NULL;
216 const char *foundtxt;
217 int found = 0;
218 int i;
219
220 output[0] = '\0';
221 while ((foundtxt = cmdtab_search_one(name, &startpos))) {
222 startpos = startpos->next;
223 if (!found)
224 strncpy(output, foundtxt, strlen(foundtxt) + 1);
225 else {
226 for (i = 0; output[i] && foundtxt[i] &&
227 output[i] == foundtxt[i]; i++)
228 ;
229 output[i] = '\0';
230 }
231 found++;
232 }
233 if (!found)
234 return 0;
235
236 if (found > 1 && !strlen(output)) {
237 printf("\n");
238 startpos = NULL;
239 while ((foundtxt = cmdtab_search_one(name, &startpos))) {
240 cmd_info_t *hlp;
241 hlp = list_get_instance(startpos, cmd_info_t, link);
242 printf("%s - %s\n", hlp->name, hlp->description);
243 startpos = startpos->next;
244 }
245 }
246 strncpy(name, output, 128/*MAX_SYMBOL_NAME*/);
247 return found;
248}
249
250static char *clever_readline(const char *prompt, indev_t *input)
251{
252 static int histposition = 0;
253
254 static char tmp[MAX_CMDLINE + 1];
255 int curlen = 0, position = 0;
256 char *current = history[histposition];
257 int i;
258 char mod; /* Command Modifier */
259 char c;
260
261 printf("%s> ", prompt);
262 while (1) {
263 c = _getc(input);
264 if (c == '\n') {
265 putchar(c);
266 break;
267 }
268 if (c == '\b') { /* Backspace */
269 if (position == 0)
270 continue;
271 for (i = position; i < curlen; i++)
272 current[i - 1] = current[i];
273 curlen--;
274 position--;
275 putchar('\b');
276 for (i = position; i < curlen; i++)
277 putchar(current[i]);
278 putchar(' ');
279 rdln_print_c('\b', curlen - position + 1);
280 continue;
281 }
282 if (c == '\t') { /* Tabulator */
283 int found;
284
285 /* Move to the end of the word */
286 for (; position < curlen && current[position] != ' ';
287 position++)
288 putchar(current[position]);
289 /* Copy to tmp last word */
290 for (i = position - 1; i >= 0 && current[i] != ' '; i--)
291 ;
292 /* If word begins with * or &, skip it */
293 if (tmp[0] == '*' || tmp[0] == '&')
294 for (i = 1; tmp[i]; i++)
295 tmp[i - 1] = tmp[i];
296 i++; /* I is at the start of the word */
297 strncpy(tmp, current + i, position - i + 1);
298
299 if (i == 0) { /* Command completion */
300 found = cmdtab_compl(tmp);
301 } else { /* Symtab completion */
302 found = symtab_compl(tmp);
303 }
304
305 if (found == 0)
306 continue;
307 for (i = 0; tmp[i] && curlen < MAX_CMDLINE;
308 i++, curlen++)
309 insert_char(current, tmp[i], i + position);
310
311 if (strlen(tmp) || found == 1) { /* If we have a hint */
312 for (i = position; i < curlen; i++)
313 putchar(current[i]);
314 position += strlen(tmp);
315 /* Add space to end */
316 if (found == 1 && position == curlen &&
317 curlen < MAX_CMDLINE) {
318 current[position] = ' ';
319 curlen++;
320 position++;
321 putchar(' ');
322 }
323 } else { /* No hint, table was printed */
324 printf("%s> ", prompt);
325 for (i = 0; i < curlen; i++)
326 putchar(current[i]);
327 position += strlen(tmp);
328 }
329 rdln_print_c('\b', curlen - position);
330 continue;
331 }
332 if (c == 0x1b) { /* Special command */
333 mod = _getc(input);
334 c = _getc(input);
335
336 if (mod != 0x5b && mod != 0x4f)
337 continue;
338
339 if (c == 0x33 && _getc(input) == 0x7e) {
340 /* Delete */
341 if (position == curlen)
342 continue;
343 for (i = position + 1; i < curlen; i++) {
344 putchar(current[i]);
345 current[i - 1] = current[i];
346 }
347 putchar(' ');
348 rdln_print_c('\b', curlen - position);
349 curlen--;
350 } else if (c == 0x48) { /* Home */
351 rdln_print_c('\b', position);
352 position = 0;
353 } else if (c == 0x46) { /* End */
354 for (i = position; i < curlen; i++)
355 putchar(current[i]);
356 position = curlen;
357 } else if (c == 0x44) { /* Left */
358 if (position > 0) {
359 putchar('\b');
360 position--;
361 }
362 continue;
363 } else if (c == 0x43) { /* Right */
364 if (position < curlen) {
365 putchar(current[position]);
366 position++;
367 }
368 continue;
369 } else if (c == 0x41 || c == 0x42) {
370 /* Up, down */
371 rdln_print_c('\b', position);
372 rdln_print_c(' ', curlen);
373 rdln_print_c('\b', curlen);
374 if (c == 0x41) /* Up */
375 histposition--;
376 else
377 histposition++;
378 if (histposition < 0) {
379 histposition = KCONSOLE_HISTORY - 1;
380 } else {
381 histposition =
382 histposition % KCONSOLE_HISTORY;
383 }
384 current = history[histposition];
385 printf("%s", current);
386 curlen = strlen(current);
387 position = curlen;
388 continue;
389 }
390 continue;
391 }
392 if (curlen >= MAX_CMDLINE)
393 continue;
394
395 insert_char(current, c, position);
396
397 curlen++;
398 for (i = position; i < curlen; i++)
399 putchar(current[i]);
400 position++;
401 rdln_print_c('\b',curlen - position);
402 }
403 if (curlen) {
404 histposition++;
405 histposition = histposition % KCONSOLE_HISTORY;
406 }
407 current[curlen] = '\0';
408 return current;
409}
410
411bool kconsole_check_poll(void)
412{
413 return check_poll(stdin);
414}
415
416/** Kernel console prompt.
417 *
418 * @param prompt Kernel console prompt (e.g kconsole/panic).
419 * @param msg Message to display in the beginning.
420 * @param kcon Wait for keypress to show the prompt
421 * and never exit.
422 *
423 */
424void kconsole(char *prompt, char *msg, bool kcon)
425{
426 cmd_info_t *cmd_info;
427 count_t len;
428 char *cmdline;
429
430 if (!stdin) {
431 LOG("No stdin for kernel console");
432 return;
433 }
434
435 if (msg)
436 printf("%s", msg);
437
438 if (kcon)
439 _getc(stdin);
440 else
441 printf("Type \"exit\" to leave the console.\n");
442
443 while (true) {
444 cmdline = clever_readline((char *) prompt, stdin);
445 len = strlen(cmdline);
446 if (!len)
447 continue;
448
449 if ((!kcon) && (len == 4) && (strncmp(cmdline, "exit", 4) == 0))
450 break;
451
452 cmd_info = parse_cmdline(cmdline, len);
453 if (!cmd_info)
454 continue;
455
456 (void) cmd_info->func(cmd_info->argv);
457 }
458}
459
460/** Kernel console managing thread.
461 *
462 */
463void kconsole_thread(void *data)
464{
465 kconsole("kconsole", "Kernel console ready (press any key to activate)\n", true);
466}
467
468static int parse_int_arg(char *text, size_t len, unative_t *result)
469{
470 uintptr_t symaddr;
471 bool isaddr = false;
472 bool isptr = false;
473 int rc;
474
475 static char symname[MAX_SYMBOL_NAME];
476
477 /* If we get a name, try to find it in symbol table */
478 if (text[0] == '&') {
479 isaddr = true;
480 text++;
481 len--;
482 } else if (text[0] == '*') {
483 isptr = true;
484 text++;
485 len--;
486 }
487 if (text[0] < '0' || text[0] > '9') {
488 strncpy(symname, text, min(len + 1, MAX_SYMBOL_NAME));
489 rc = symtab_addr_lookup(symname, &symaddr);
490 switch (rc) {
491 case ENOENT:
492 printf("Symbol %s not found.\n", symname);
493 return -1;
494 case EOVERFLOW:
495 printf("Duplicate symbol %s.\n", symname);
496 symtab_print_search(symname);
497 return -1;
498 default:
499 printf("No symbol information available.\n");
500 return -1;
501 }
502
503 if (isaddr)
504 *result = (unative_t)symaddr;
505 else if (isptr)
506 *result = **((unative_t **)symaddr);
507 else
508 *result = *((unative_t *)symaddr);
509 } else { /* It's a number - convert it */
510 *result = atoi(text);
511 if (isptr)
512 *result = *((unative_t *)*result);
513 }
514
515 return 0;
516}
517
518/** Parse command line.
519 *
520 * @param cmdline Command line as read from input device.
521 * @param len Command line length.
522 *
523 * @return Structure describing the command.
524 */
525cmd_info_t *parse_cmdline(char *cmdline, size_t len)
526{
527 index_t start = 0, end = 0;
528 cmd_info_t *cmd = NULL;
529 link_t *cur;
530 count_t i;
531 int error = 0;
532
533 if (!parse_argument(cmdline, len, &start, &end)) {
534 /* Command line did not contain alphanumeric word. */
535 return NULL;
536 }
537
538 spinlock_lock(&cmd_lock);
539
540 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
541 cmd_info_t *hlp;
542
543 hlp = list_get_instance(cur, cmd_info_t, link);
544 spinlock_lock(&hlp->lock);
545
546 if (strncmp(hlp->name, &cmdline[start], max(strlen(hlp->name),
547 end - start + 1)) == 0) {
548 cmd = hlp;
549 break;
550 }
551
552 spinlock_unlock(&hlp->lock);
553 }
554
555 spinlock_unlock(&cmd_lock);
556
557 if (!cmd) {
558 /* Unknown command. */
559 printf("Unknown command.\n");
560 return NULL;
561 }
562
563 /* cmd == hlp is locked */
564
565 /*
566 * The command line must be further analyzed and
567 * the parameters therefrom must be matched and
568 * converted to those specified in the cmd info
569 * structure.
570 */
571
572 for (i = 0; i < cmd->argc; i++) {
573 char *buf;
574 start = end + 1;
575 if (!parse_argument(cmdline, len, &start, &end)) {
576 printf("Too few arguments.\n");
577 spinlock_unlock(&cmd->lock);
578 return NULL;
579 }
580
581 error = 0;
582 switch (cmd->argv[i].type) {
583 case ARG_TYPE_STRING:
584 buf = (char *) cmd->argv[i].buffer;
585 strncpy(buf, (const char *) &cmdline[start],
586 min((end - start) + 2, cmd->argv[i].len));
587 buf[min((end - start) + 1, cmd->argv[i].len - 1)] =
588 '\0';
589 break;
590 case ARG_TYPE_INT:
591 if (parse_int_arg(cmdline + start, end - start + 1,
592 &cmd->argv[i].intval))
593 error = 1;
594 break;
595 case ARG_TYPE_VAR:
596 if (start != end && cmdline[start] == '"' &&
597 cmdline[end] == '"') {
598 buf = (char *) cmd->argv[i].buffer;
599 strncpy(buf, (const char *) &cmdline[start + 1],
600 min((end-start), cmd->argv[i].len));
601 buf[min((end - start), cmd->argv[i].len - 1)] =
602 '\0';
603 cmd->argv[i].intval = (unative_t) buf;
604 cmd->argv[i].vartype = ARG_TYPE_STRING;
605 } else if (!parse_int_arg(cmdline + start,
606 end - start + 1, &cmd->argv[i].intval)) {
607 cmd->argv[i].vartype = ARG_TYPE_INT;
608 } else {
609 printf("Unrecognized variable argument.\n");
610 error = 1;
611 }
612 break;
613 case ARG_TYPE_INVALID:
614 default:
615 printf("invalid argument type\n");
616 error = 1;
617 break;
618 }
619 }
620
621 if (error) {
622 spinlock_unlock(&cmd->lock);
623 return NULL;
624 }
625
626 start = end + 1;
627 if (parse_argument(cmdline, len, &start, &end)) {
628 printf("Too many arguments.\n");
629 spinlock_unlock(&cmd->lock);
630 return NULL;
631 }
632
633 spinlock_unlock(&cmd->lock);
634 return cmd;
635}
636
637/** Parse argument.
638 *
639 * Find start and end positions of command line argument.
640 *
641 * @param cmdline Command line as read from the input device.
642 * @param len Number of characters in cmdline.
643 * @param start On entry, 'start' contains pointer to the index
644 * of first unprocessed character of cmdline.
645 * On successful exit, it marks beginning of the next argument.
646 * @param end Undefined on entry. On exit, 'end' points to the last character
647 * of the next argument.
648 *
649 * @return false on failure, true on success.
650 */
651bool parse_argument(char *cmdline, size_t len, index_t *start, index_t *end)
652{
653 index_t i;
654 bool found_start = false;
655
656 ASSERT(start != NULL);
657 ASSERT(end != NULL);
658
659 for (i = *start; i < len; i++) {
660 if (!found_start) {
661 if (isspace(cmdline[i]))
662 (*start)++;
663 else
664 found_start = true;
665 } else {
666 if (isspace(cmdline[i]))
667 break;
668 }
669 }
670 *end = i - 1;
671
672 return found_start;
673}
674
675/** @}
676 */
Note: See TracBrowser for help on using the repository browser.