source: mainline/generic/src/console/kconsole.c@ c43fa55

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c43fa55 was 80d2bdb, checked in by Ondrej Palkovsky <ondrap@…>, 20 years ago

We are now almost -Wall clean.

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