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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a5e5030 was 84afc7b, checked in by Martin Decky <martin@…>, 17 years ago

as kernel little brother drivers are not needed anymore, the device numbers do not have to be correlated between kernel and uspace in any way
introduce new syscall sys_device_assign_devno() for generating system-wide unique device numbers for uspace

  • Property mode set to 100644
File size: 16.7 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/*
91 * For now, we use 0 as INR.
92 * However, it is therefore desirable to have architecture specific
93 * definition of KCONSOLE_VIRT_INR in the future.
94 */
95#define KCONSOLE_VIRT_INR 0
96
97bool kconsole_notify = false;
98irq_t kconsole_irq;
99
100
101/** Allways refuse IRQ ownership.
102 *
103 * This is not a real IRQ, so we always decline.
104 *
105 * @return Always returns IRQ_DECLINE.
106 *
107 */
108static irq_ownership_t kconsole_claim(irq_t *irq)
109{
110 return IRQ_DECLINE;
111}
112
113
114/** Initialize kconsole data structures
115 *
116 * This is the most basic initialization, almost no
117 * other kernel subsystem is ready yet.
118 *
119 */
120void kconsole_init(void)
121{
122 unsigned int i;
123
124 cmd_init();
125 for (i = 0; i < KCONSOLE_HISTORY; i++)
126 history[i][0] = '\0';
127}
128
129
130/** Initialize kconsole notification mechanism
131 *
132 * Initialize the virtual IRQ notification mechanism.
133 *
134 */
135void kconsole_notify_init(void)
136{
137 sysinfo_set_item_val("kconsole.present", NULL, true);
138 sysinfo_set_item_val("kconsole.inr", NULL, KCONSOLE_VIRT_INR);
139
140 irq_initialize(&kconsole_irq);
141 kconsole_irq.devno = device_assign_devno();
142 kconsole_irq.inr = KCONSOLE_VIRT_INR;
143 kconsole_irq.claim = kconsole_claim;
144 irq_register(&kconsole_irq);
145
146 kconsole_notify = true;
147}
148
149
150/** Register kconsole command.
151 *
152 * @param cmd Structure describing the command.
153 *
154 * @return 0 on failure, 1 on success.
155 */
156int cmd_register(cmd_info_t *cmd)
157{
158 link_t *cur;
159
160 spinlock_lock(&cmd_lock);
161
162 /*
163 * Make sure the command is not already listed.
164 */
165 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
166 cmd_info_t *hlp;
167
168 hlp = list_get_instance(cur, cmd_info_t, link);
169
170 if (hlp == cmd) {
171 /* The command is already there. */
172 spinlock_unlock(&cmd_lock);
173 return 0;
174 }
175
176 /* Avoid deadlock. */
177 if (hlp < cmd) {
178 spinlock_lock(&hlp->lock);
179 spinlock_lock(&cmd->lock);
180 } else {
181 spinlock_lock(&cmd->lock);
182 spinlock_lock(&hlp->lock);
183 }
184 if ((strncmp(hlp->name, cmd->name, max(strlen(cmd->name),
185 strlen(hlp->name))) == 0)) {
186 /* The command is already there. */
187 spinlock_unlock(&hlp->lock);
188 spinlock_unlock(&cmd->lock);
189 spinlock_unlock(&cmd_lock);
190 return 0;
191 }
192
193 spinlock_unlock(&hlp->lock);
194 spinlock_unlock(&cmd->lock);
195 }
196
197 /*
198 * Now the command can be added.
199 */
200 list_append(&cmd->link, &cmd_head);
201
202 spinlock_unlock(&cmd_lock);
203 return 1;
204}
205
206/** Print count times a character */
207static void rdln_print_c(char ch, int count)
208{
209 int i;
210 for (i = 0; i < count; i++)
211 putchar(ch);
212}
213
214/** Insert character to string */
215static void insert_char(char *str, char ch, int pos)
216{
217 int i;
218
219 for (i = strlen(str); i > pos; i--)
220 str[i] = str[i - 1];
221 str[pos] = ch;
222}
223
224/** Try to find a command beginning with prefix */
225static const char *cmdtab_search_one(const char *name,link_t **startpos)
226{
227 size_t namelen = strlen(name);
228 const char *curname;
229
230 spinlock_lock(&cmd_lock);
231
232 if (!*startpos)
233 *startpos = cmd_head.next;
234
235 for (; *startpos != &cmd_head; *startpos = (*startpos)->next) {
236 cmd_info_t *hlp;
237 hlp = list_get_instance(*startpos, cmd_info_t, link);
238
239 curname = hlp->name;
240 if (strlen(curname) < namelen)
241 continue;
242 if (strncmp(curname, name, namelen) == 0) {
243 spinlock_unlock(&cmd_lock);
244 return curname+namelen;
245 }
246 }
247 spinlock_unlock(&cmd_lock);
248 return NULL;
249}
250
251
252/** Command completion of the commands
253 *
254 * @param name - string to match, changed to hint on exit
255 * @return number of found matches
256 */
257static int cmdtab_compl(char *name)
258{
259 static char output[/*MAX_SYMBOL_NAME*/128 + 1];
260 link_t *startpos = NULL;
261 const char *foundtxt;
262 int found = 0;
263 int i;
264
265 output[0] = '\0';
266 while ((foundtxt = cmdtab_search_one(name, &startpos))) {
267 startpos = startpos->next;
268 if (!found)
269 strncpy(output, foundtxt, strlen(foundtxt) + 1);
270 else {
271 for (i = 0; output[i] && foundtxt[i] &&
272 output[i] == foundtxt[i]; i++)
273 ;
274 output[i] = '\0';
275 }
276 found++;
277 }
278 if (!found)
279 return 0;
280
281 if (found > 1 && !strlen(output)) {
282 printf("\n");
283 startpos = NULL;
284 while ((foundtxt = cmdtab_search_one(name, &startpos))) {
285 cmd_info_t *hlp;
286 hlp = list_get_instance(startpos, cmd_info_t, link);
287 printf("%s - %s\n", hlp->name, hlp->description);
288 startpos = startpos->next;
289 }
290 }
291 strncpy(name, output, 128/*MAX_SYMBOL_NAME*/);
292 return found;
293}
294
295static char *clever_readline(const char *prompt, indev_t *input)
296{
297 static int histposition = 0;
298
299 static char tmp[MAX_CMDLINE + 1];
300 int curlen = 0, position = 0;
301 char *current = history[histposition];
302 int i;
303 char mod; /* Command Modifier */
304 char c;
305
306 printf("%s> ", prompt);
307 while (1) {
308 c = _getc(input);
309 if (c == '\n') {
310 putchar(c);
311 break;
312 }
313 if (c == '\b') { /* Backspace */
314 if (position == 0)
315 continue;
316 for (i = position; i < curlen; i++)
317 current[i - 1] = current[i];
318 curlen--;
319 position--;
320 putchar('\b');
321 for (i = position; i < curlen; i++)
322 putchar(current[i]);
323 putchar(' ');
324 rdln_print_c('\b', curlen - position + 1);
325 continue;
326 }
327 if (c == '\t') { /* Tabulator */
328 int found;
329
330 /* Move to the end of the word */
331 for (; position < curlen && current[position] != ' ';
332 position++)
333 putchar(current[position]);
334 /* Copy to tmp last word */
335 for (i = position - 1; i >= 0 && current[i] != ' '; i--)
336 ;
337 /* If word begins with * or &, skip it */
338 if (tmp[0] == '*' || tmp[0] == '&')
339 for (i = 1; tmp[i]; i++)
340 tmp[i - 1] = tmp[i];
341 i++; /* I is at the start of the word */
342 strncpy(tmp, current + i, position - i + 1);
343
344 if (i == 0) { /* Command completion */
345 found = cmdtab_compl(tmp);
346 } else { /* Symtab completion */
347 found = symtab_compl(tmp);
348 }
349
350 if (found == 0)
351 continue;
352 for (i = 0; tmp[i] && curlen < MAX_CMDLINE;
353 i++, curlen++)
354 insert_char(current, tmp[i], i + position);
355
356 if (strlen(tmp) || found == 1) { /* If we have a hint */
357 for (i = position; i < curlen; i++)
358 putchar(current[i]);
359 position += strlen(tmp);
360 /* Add space to end */
361 if (found == 1 && position == curlen &&
362 curlen < MAX_CMDLINE) {
363 current[position] = ' ';
364 curlen++;
365 position++;
366 putchar(' ');
367 }
368 } else { /* No hint, table was printed */
369 printf("%s> ", prompt);
370 for (i = 0; i < curlen; i++)
371 putchar(current[i]);
372 position += strlen(tmp);
373 }
374 rdln_print_c('\b', curlen - position);
375 continue;
376 }
377 if (c == 0x1b) { /* Special command */
378 mod = _getc(input);
379 c = _getc(input);
380
381 if (mod != 0x5b && mod != 0x4f)
382 continue;
383
384 if (c == 0x33 && _getc(input) == 0x7e) {
385 /* Delete */
386 if (position == curlen)
387 continue;
388 for (i = position + 1; i < curlen; i++) {
389 putchar(current[i]);
390 current[i - 1] = current[i];
391 }
392 putchar(' ');
393 rdln_print_c('\b', curlen - position);
394 curlen--;
395 } else if (c == 0x48) { /* Home */
396 rdln_print_c('\b', position);
397 position = 0;
398 } else if (c == 0x46) { /* End */
399 for (i = position; i < curlen; i++)
400 putchar(current[i]);
401 position = curlen;
402 } else if (c == 0x44) { /* Left */
403 if (position > 0) {
404 putchar('\b');
405 position--;
406 }
407 continue;
408 } else if (c == 0x43) { /* Right */
409 if (position < curlen) {
410 putchar(current[position]);
411 position++;
412 }
413 continue;
414 } else if (c == 0x41 || c == 0x42) {
415 /* Up, down */
416 rdln_print_c('\b', position);
417 rdln_print_c(' ', curlen);
418 rdln_print_c('\b', curlen);
419 if (c == 0x41) /* Up */
420 histposition--;
421 else
422 histposition++;
423 if (histposition < 0) {
424 histposition = KCONSOLE_HISTORY - 1;
425 } else {
426 histposition =
427 histposition % KCONSOLE_HISTORY;
428 }
429 current = history[histposition];
430 printf("%s", current);
431 curlen = strlen(current);
432 position = curlen;
433 continue;
434 }
435 continue;
436 }
437 if (curlen >= MAX_CMDLINE)
438 continue;
439
440 insert_char(current, c, position);
441
442 curlen++;
443 for (i = position; i < curlen; i++)
444 putchar(current[i]);
445 position++;
446 rdln_print_c('\b',curlen - position);
447 }
448 if (curlen) {
449 histposition++;
450 histposition = histposition % KCONSOLE_HISTORY;
451 }
452 current[curlen] = '\0';
453 return current;
454}
455
456bool kconsole_check_poll(void)
457{
458 return check_poll(stdin);
459}
460
461/** Kernel console prompt.
462 *
463 * @param prompt Kernel console prompt (e.g kconsole/panic).
464 * @param msg Message to display in the beginning.
465 * @param kcon Wait for keypress to show the prompt
466 * and never exit.
467 *
468 */
469void kconsole(char *prompt, char *msg, bool kcon)
470{
471 cmd_info_t *cmd_info;
472 count_t len;
473 char *cmdline;
474
475 if (!stdin) {
476 LOG("No stdin for kernel console");
477 return;
478 }
479
480 if (msg)
481 printf("%s", msg);
482
483 if (kcon)
484 _getc(stdin);
485 else
486 printf("Type \"exit\" to leave the console.\n");
487
488 while (true) {
489 cmdline = clever_readline((char *) prompt, stdin);
490 len = strlen(cmdline);
491 if (!len)
492 continue;
493
494 if ((!kcon) && (len == 4) && (strncmp(cmdline, "exit", 4) == 0))
495 break;
496
497 cmd_info = parse_cmdline(cmdline, len);
498 if (!cmd_info)
499 continue;
500
501 (void) cmd_info->func(cmd_info->argv);
502 }
503}
504
505/** Kernel console managing thread.
506 *
507 */
508void kconsole_thread(void *data)
509{
510 kconsole("kconsole", "Kernel console ready (press any key to activate)\n", true);
511}
512
513static int parse_int_arg(char *text, size_t len, unative_t *result)
514{
515 uintptr_t symaddr;
516 bool isaddr = false;
517 bool isptr = false;
518 int rc;
519
520 static char symname[MAX_SYMBOL_NAME];
521
522 /* If we get a name, try to find it in symbol table */
523 if (text[0] == '&') {
524 isaddr = true;
525 text++;
526 len--;
527 } else if (text[0] == '*') {
528 isptr = true;
529 text++;
530 len--;
531 }
532 if (text[0] < '0' || text[0] > '9') {
533 strncpy(symname, text, min(len + 1, MAX_SYMBOL_NAME));
534 rc = symtab_addr_lookup(symname, &symaddr);
535 switch (rc) {
536 case ENOENT:
537 printf("Symbol %s not found.\n", symname);
538 return -1;
539 case EOVERFLOW:
540 printf("Duplicate symbol %s.\n", symname);
541 symtab_print_search(symname);
542 return -1;
543 default:
544 printf("No symbol information available.\n");
545 return -1;
546 }
547
548 if (isaddr)
549 *result = (unative_t)symaddr;
550 else if (isptr)
551 *result = **((unative_t **)symaddr);
552 else
553 *result = *((unative_t *)symaddr);
554 } else { /* It's a number - convert it */
555 *result = atoi(text);
556 if (isptr)
557 *result = *((unative_t *)*result);
558 }
559
560 return 0;
561}
562
563/** Parse command line.
564 *
565 * @param cmdline Command line as read from input device.
566 * @param len Command line length.
567 *
568 * @return Structure describing the command.
569 */
570cmd_info_t *parse_cmdline(char *cmdline, size_t len)
571{
572 index_t start = 0, end = 0;
573 cmd_info_t *cmd = NULL;
574 link_t *cur;
575 count_t i;
576 int error = 0;
577
578 if (!parse_argument(cmdline, len, &start, &end)) {
579 /* Command line did not contain alphanumeric word. */
580 return NULL;
581 }
582
583 spinlock_lock(&cmd_lock);
584
585 for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
586 cmd_info_t *hlp;
587
588 hlp = list_get_instance(cur, cmd_info_t, link);
589 spinlock_lock(&hlp->lock);
590
591 if (strncmp(hlp->name, &cmdline[start], max(strlen(hlp->name),
592 end - start + 1)) == 0) {
593 cmd = hlp;
594 break;
595 }
596
597 spinlock_unlock(&hlp->lock);
598 }
599
600 spinlock_unlock(&cmd_lock);
601
602 if (!cmd) {
603 /* Unknown command. */
604 printf("Unknown command.\n");
605 return NULL;
606 }
607
608 /* cmd == hlp is locked */
609
610 /*
611 * The command line must be further analyzed and
612 * the parameters therefrom must be matched and
613 * converted to those specified in the cmd info
614 * structure.
615 */
616
617 for (i = 0; i < cmd->argc; i++) {
618 char *buf;
619 start = end + 1;
620 if (!parse_argument(cmdline, len, &start, &end)) {
621 printf("Too few arguments.\n");
622 spinlock_unlock(&cmd->lock);
623 return NULL;
624 }
625
626 error = 0;
627 switch (cmd->argv[i].type) {
628 case ARG_TYPE_STRING:
629 buf = (char *) cmd->argv[i].buffer;
630 strncpy(buf, (const char *) &cmdline[start],
631 min((end - start) + 2, cmd->argv[i].len));
632 buf[min((end - start) + 1, cmd->argv[i].len - 1)] =
633 '\0';
634 break;
635 case ARG_TYPE_INT:
636 if (parse_int_arg(cmdline + start, end - start + 1,
637 &cmd->argv[i].intval))
638 error = 1;
639 break;
640 case ARG_TYPE_VAR:
641 if (start != end && cmdline[start] == '"' &&
642 cmdline[end] == '"') {
643 buf = (char *) cmd->argv[i].buffer;
644 strncpy(buf, (const char *) &cmdline[start + 1],
645 min((end-start), cmd->argv[i].len));
646 buf[min((end - start), cmd->argv[i].len - 1)] =
647 '\0';
648 cmd->argv[i].intval = (unative_t) buf;
649 cmd->argv[i].vartype = ARG_TYPE_STRING;
650 } else if (!parse_int_arg(cmdline + start,
651 end - start + 1, &cmd->argv[i].intval)) {
652 cmd->argv[i].vartype = ARG_TYPE_INT;
653 } else {
654 printf("Unrecognized variable argument.\n");
655 error = 1;
656 }
657 break;
658 case ARG_TYPE_INVALID:
659 default:
660 printf("invalid argument type\n");
661 error = 1;
662 break;
663 }
664 }
665
666 if (error) {
667 spinlock_unlock(&cmd->lock);
668 return NULL;
669 }
670
671 start = end + 1;
672 if (parse_argument(cmdline, len, &start, &end)) {
673 printf("Too many arguments.\n");
674 spinlock_unlock(&cmd->lock);
675 return NULL;
676 }
677
678 spinlock_unlock(&cmd->lock);
679 return cmd;
680}
681
682/** Parse argument.
683 *
684 * Find start and end positions of command line argument.
685 *
686 * @param cmdline Command line as read from the input device.
687 * @param len Number of characters in cmdline.
688 * @param start On entry, 'start' contains pointer to the index
689 * of first unprocessed character of cmdline.
690 * On successful exit, it marks beginning of the next argument.
691 * @param end Undefined on entry. On exit, 'end' points to the last character
692 * of the next argument.
693 *
694 * @return false on failure, true on success.
695 */
696bool parse_argument(char *cmdline, size_t len, index_t *start, index_t *end)
697{
698 index_t i;
699 bool found_start = false;
700
701 ASSERT(start != NULL);
702 ASSERT(end != NULL);
703
704 for (i = *start; i < len; i++) {
705 if (!found_start) {
706 if (isspace(cmdline[i]))
707 (*start)++;
708 else
709 found_start = true;
710 } else {
711 if (isspace(cmdline[i]))
712 break;
713 }
714 }
715 *end = i - 1;
716
717 return found_start;
718}
719
720/** @}
721 */
Note: See TracBrowser for help on using the repository browser.