source: mainline/kernel/generic/src/console/cmd.c@ 8a64e81e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8a64e81e was 8a64e81e, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

workq: Add work queues: allow blocking work items, queuing items from interrupt handlers.

  • Property mode set to 100644
File size: 28.2 KB
RevLine 
[442d0ae]1/*
[df4ed85]2 * Copyright (c) 2005 Jakub Jermar
[442d0ae]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
[06e1e95]29/** @addtogroup genericconsole
[b45c443]30 * @{
31 */
32
[442d0ae]33/**
[cb01e1e]34 * @file cmd.c
35 * @brief Kernel console command wrappers.
[cf26ba9]36 *
[442d0ae]37 * This file is meant to contain all wrapper functions for
38 * all kconsole commands. The point is in separating
39 * kconsole specific wrappers from kconsole-unaware functions
40 * from other subsystems.
41 */
42
43#include <console/cmd.h>
[41d33ac]44#include <console/console.h>
[442d0ae]45#include <console/kconsole.h>
46#include <print.h>
47#include <panic.h>
[d99c1d2]48#include <typedefs.h>
[5c9a08b]49#include <adt/list.h>
[442d0ae]50#include <arch.h>
[f74bbaf]51#include <config.h>
[442d0ae]52#include <func.h>
[19f857a]53#include <str.h>
[442d0ae]54#include <macros.h>
55#include <debug.h>
[0132630]56#include <cpu.h>
[442d0ae]57#include <mm/tlb.h>
58#include <arch/mm/tlb.h>
[80bff342]59#include <mm/frame.h>
[0132630]60#include <main/version.h>
[4e147a6]61#include <mm/slab.h>
[10e16a7]62#include <proc/scheduler.h>
[55ab0f1]63#include <proc/thread.h>
[37c57f2]64#include <proc/task.h>
[c4e4507]65#include <ipc/ipc.h>
[62939f7]66#include <ipc/irq.h>
[13a638d]67#include <ipc/event.h>
[b3631bc]68#include <sysinfo/sysinfo.h>
[e2b762ec]69#include <symtab.h>
[8a64e81e]70#include <synch/workqueue.h>
[e16e0d59]71#include <errno.h>
[e2b762ec]72
[319e60e]73#ifdef CONFIG_TEST
74#include <test.h>
75#endif
76
[b45c443]77/* Data and methods for 'help' command. */
[442d0ae]78static int cmd_help(cmd_arg_t *argv);
79static cmd_info_t help_info = {
80 .name = "help",
[df58e44]81 .description = "List supported commands.",
[442d0ae]82 .func = cmd_help,
83 .argc = 0
84};
85
[df58e44]86/* Data and methods for 'reboot' command. */
[f74bbaf]87static int cmd_reboot(cmd_arg_t *argv);
88static cmd_info_t reboot_info = {
89 .name = "reboot",
[df58e44]90 .description = "Reboot system.",
[f74bbaf]91 .func = cmd_reboot,
[e07fe0c]92 .argc = 0
93};
94
[df58e44]95/* Data and methods for 'uptime' command. */
[4b662f8c]96static int cmd_uptime(cmd_arg_t *argv);
97static cmd_info_t uptime_info = {
98 .name = "uptime",
[df58e44]99 .description = "Show system uptime.",
[4b662f8c]100 .func = cmd_uptime,
101 .argc = 0
102};
103
[df58e44]104/* Data and methods for 'continue' command. */
[41d33ac]105static int cmd_continue(cmd_arg_t *argv);
106static cmd_info_t continue_info = {
107 .name = "continue",
[319e60e]108 .description = "Return console back to userspace.",
[41d33ac]109 .func = cmd_continue,
110 .argc = 0
111};
112
[319e60e]113#ifdef CONFIG_TEST
[5b7a107]114
[df58e44]115/* Data and methods for 'test' command. */
[319e60e]116static char test_buf[MAX_CMDLINE + 1];
117static int cmd_test(cmd_arg_t *argv);
118static cmd_arg_t test_argv[] = {
119 {
[851f33a]120 .type = ARG_TYPE_STRING_OPTIONAL,
[319e60e]121 .buffer = test_buf,
122 .len = sizeof(test_buf)
123 }
124};
125static cmd_info_t test_info = {
126 .name = "test",
[df58e44]127 .description = "<test> List kernel tests or run a test.",
[319e60e]128 .func = cmd_test,
129 .argc = 1,
130 .argv = test_argv
131};
[95155b0c]132
[df58e44]133/* Data and methods for 'bench' command. */
[95155b0c]134static int cmd_bench(cmd_arg_t *argv);
135static cmd_arg_t bench_argv[] = {
136 {
137 .type = ARG_TYPE_STRING,
138 .buffer = test_buf,
139 .len = sizeof(test_buf)
140 },
141 {
142 .type = ARG_TYPE_INT,
143 }
144};
145static cmd_info_t bench_info = {
146 .name = "bench",
[df58e44]147 .description = "<test> <count> Run kernel test as benchmark.",
[95155b0c]148 .func = cmd_bench,
149 .argc = 2,
150 .argv = bench_argv
151};
[5b7a107]152
153#endif /* CONFIG_TEST */
[319e60e]154
[b45c443]155/* Data and methods for 'description' command. */
[442d0ae]156static int cmd_desc(cmd_arg_t *argv);
157static void desc_help(void);
[df58e44]158static char desc_buf[MAX_CMDLINE + 1];
[442d0ae]159static cmd_arg_t desc_argv = {
160 .type = ARG_TYPE_STRING,
161 .buffer = desc_buf,
162 .len = sizeof(desc_buf)
163};
164static cmd_info_t desc_info = {
165 .name = "describe",
[df58e44]166 .description = "<command> Describe specified command.",
[442d0ae]167 .help = desc_help,
168 .func = cmd_desc,
169 .argc = 1,
170 .argv = &desc_argv
171};
172
[b45c443]173/* Data and methods for 'symaddr' command. */
[442d0ae]174static int cmd_symaddr(cmd_arg_t *argv);
[df58e44]175static char symaddr_buf[MAX_CMDLINE + 1];
[442d0ae]176static cmd_arg_t symaddr_argv = {
177 .type = ARG_TYPE_STRING,
178 .buffer = symaddr_buf,
179 .len = sizeof(symaddr_buf)
180};
181static cmd_info_t symaddr_info = {
182 .name = "symaddr",
[df58e44]183 .description = "<symbol> Return symbol address.",
[442d0ae]184 .func = cmd_symaddr,
185 .argc = 1,
186 .argv = &symaddr_argv
187};
188
[df58e44]189/* Data and methods for 'set4' command. */
190static char set_buf[MAX_CMDLINE + 1];
[ba276f7]191static int cmd_set4(cmd_arg_t *argv);
192static cmd_arg_t set4_argv[] = {
193 {
194 .type = ARG_TYPE_STRING,
195 .buffer = set_buf,
196 .len = sizeof(set_buf)
197 },
198 {
199 .type = ARG_TYPE_INT
200 }
201};
202static cmd_info_t set4_info = {
203 .name = "set4",
[df58e44]204 .description = "<addr> <value> Set 4B memory location to a value.",
[ba276f7]205 .func = cmd_set4,
206 .argc = 2,
207 .argv = set4_argv
208};
209
[c0f13d2]210/* Data and methods for 'call0' and 'mcall0' command. */
[e5dbbe5]211static char call0_buf[MAX_CMDLINE + 1];
212static char carg1_buf[MAX_CMDLINE + 1];
213static char carg2_buf[MAX_CMDLINE + 1];
214static char carg3_buf[MAX_CMDLINE + 1];
[442d0ae]215
216static int cmd_call0(cmd_arg_t *argv);
217static cmd_arg_t call0_argv = {
218 .type = ARG_TYPE_STRING,
219 .buffer = call0_buf,
220 .len = sizeof(call0_buf)
221};
222static cmd_info_t call0_info = {
223 .name = "call0",
[df58e44]224 .description = "<function> Call function().",
[442d0ae]225 .func = cmd_call0,
226 .argc = 1,
227 .argv = &call0_argv
228};
229
[e5dbbe5]230/* Data and methods for 'mcall0' command. */
231static int cmd_mcall0(cmd_arg_t *argv);
232static cmd_arg_t mcall0_argv = {
233 .type = ARG_TYPE_STRING,
234 .buffer = call0_buf,
235 .len = sizeof(call0_buf)
236};
237static cmd_info_t mcall0_info = {
238 .name = "mcall0",
[df58e44]239 .description = "<function> Call function() on each CPU.",
[e5dbbe5]240 .func = cmd_mcall0,
241 .argc = 1,
242 .argv = &mcall0_argv
243};
244
[b45c443]245/* Data and methods for 'call1' command. */
[442d0ae]246static int cmd_call1(cmd_arg_t *argv);
247static cmd_arg_t call1_argv[] = {
248 {
249 .type = ARG_TYPE_STRING,
250 .buffer = call0_buf,
251 .len = sizeof(call0_buf)
252 },
253 {
254 .type = ARG_TYPE_VAR,
255 .buffer = carg1_buf,
256 .len = sizeof(carg1_buf)
257 }
258};
259static cmd_info_t call1_info = {
260 .name = "call1",
[df58e44]261 .description = "<function> <arg1> Call function(arg1).",
[442d0ae]262 .func = cmd_call1,
263 .argc = 2,
264 .argv = call1_argv
265};
266
[b45c443]267/* Data and methods for 'call2' command. */
[442d0ae]268static int cmd_call2(cmd_arg_t *argv);
269static cmd_arg_t call2_argv[] = {
270 {
271 .type = ARG_TYPE_STRING,
272 .buffer = call0_buf,
273 .len = sizeof(call0_buf)
274 },
275 {
276 .type = ARG_TYPE_VAR,
277 .buffer = carg1_buf,
278 .len = sizeof(carg1_buf)
279 },
280 {
281 .type = ARG_TYPE_VAR,
282 .buffer = carg2_buf,
283 .len = sizeof(carg2_buf)
284 }
285};
286static cmd_info_t call2_info = {
287 .name = "call2",
[df58e44]288 .description = "<function> <arg1> <arg2> Call function(arg1, arg2).",
[442d0ae]289 .func = cmd_call2,
290 .argc = 3,
291 .argv = call2_argv
292};
293
[b45c443]294/* Data and methods for 'call3' command. */
[442d0ae]295static int cmd_call3(cmd_arg_t *argv);
296static cmd_arg_t call3_argv[] = {
297 {
298 .type = ARG_TYPE_STRING,
299 .buffer = call0_buf,
300 .len = sizeof(call0_buf)
301 },
302 {
303 .type = ARG_TYPE_VAR,
304 .buffer = carg1_buf,
305 .len = sizeof(carg1_buf)
306 },
307 {
308 .type = ARG_TYPE_VAR,
309 .buffer = carg2_buf,
310 .len = sizeof(carg2_buf)
311 },
312 {
313 .type = ARG_TYPE_VAR,
314 .buffer = carg3_buf,
315 .len = sizeof(carg3_buf)
316 }
317
318};
319static cmd_info_t call3_info = {
320 .name = "call3",
[df58e44]321 .description = "<function> <arg1> <arg2> <arg3> Call function(arg1, arg2, arg3).",
[442d0ae]322 .func = cmd_call3,
323 .argc = 4,
324 .argv = call3_argv
325};
326
[b45c443]327/* Data and methods for 'halt' command. */
[442d0ae]328static int cmd_halt(cmd_arg_t *argv);
329static cmd_info_t halt_info = {
330 .name = "halt",
331 .description = "Halt the kernel.",
332 .func = cmd_halt,
333 .argc = 0
334};
335
[b07c332]336/* Data and methods for 'physmem' command. */
337static int cmd_physmem(cmd_arg_t *argv);
338cmd_info_t physmem_info = {
339 .name = "physmem",
340 .description = "Print physical memory configuration.",
341 .help = NULL,
342 .func = cmd_physmem,
343 .argc = 0,
344 .argv = NULL
345};
346
[b45c443]347/* Data and methods for 'tlb' command. */
[0132630]348static int cmd_tlb(cmd_arg_t *argv);
349cmd_info_t tlb_info = {
350 .name = "tlb",
[df58e44]351 .description = "Print TLB of the current CPU.",
[442d0ae]352 .help = NULL,
[0132630]353 .func = cmd_tlb,
[442d0ae]354 .argc = 0,
355 .argv = NULL
356};
357
[48dcc69]358static char flag_buf[MAX_CMDLINE + 1];
359
[55ab0f1]360static int cmd_threads(cmd_arg_t *argv);
[48dcc69]361static cmd_arg_t threads_argv = {
362 .type = ARG_TYPE_STRING_OPTIONAL,
363 .buffer = flag_buf,
364 .len = sizeof(flag_buf)
365};
[55ab0f1]366static cmd_info_t threads_info = {
367 .name = "threads",
[48dcc69]368 .description = "List all threads (use -a for additional information).",
[55ab0f1]369 .func = cmd_threads,
[48dcc69]370 .argc = 1,
371 .argv = &threads_argv
[55ab0f1]372};
373
[37c57f2]374static int cmd_tasks(cmd_arg_t *argv);
[c0f13d2]375static cmd_arg_t tasks_argv = {
376 .type = ARG_TYPE_STRING_OPTIONAL,
[48dcc69]377 .buffer = flag_buf,
378 .len = sizeof(flag_buf)
[c0f13d2]379};
[37c57f2]380static cmd_info_t tasks_info = {
381 .name = "tasks",
[c0f13d2]382 .description = "List all tasks (use -a for additional information).",
[37c57f2]383 .func = cmd_tasks,
[c0f13d2]384 .argc = 1,
385 .argv = &tasks_argv
[37c57f2]386};
387
[5b7a107]388#ifdef CONFIG_UDEBUG
389
[df58e44]390/* Data and methods for 'btrace' command */
391static int cmd_btrace(cmd_arg_t *argv);
392static cmd_arg_t btrace_argv = {
393 .type = ARG_TYPE_INT,
394};
395static cmd_info_t btrace_info = {
396 .name = "btrace",
397 .description = "<threadid> Show thread stack trace.",
398 .func = cmd_btrace,
399 .argc = 1,
400 .argv = &btrace_argv
401};
[80bff342]402
[5b7a107]403#endif /* CONFIG_UDEBUG */
[80bff342]404
[10e16a7]405static int cmd_sched(cmd_arg_t *argv);
406static cmd_info_t sched_info = {
407 .name = "scheduler",
[df58e44]408 .description = "Show scheduler information.",
[10e16a7]409 .func = cmd_sched,
410 .argc = 0
411};
412
[4e147a6]413static int cmd_slabs(cmd_arg_t *argv);
414static cmd_info_t slabs_info = {
415 .name = "slabs",
[dd054bc2]416 .description = "List slab caches.",
[4e147a6]417 .func = cmd_slabs,
418 .argc = 0
419};
420
[b3631bc]421static int cmd_sysinfo(cmd_arg_t *argv);
422static cmd_info_t sysinfo_info = {
423 .name = "sysinfo",
424 .description = "Dump sysinfo.",
425 .func = cmd_sysinfo,
426 .argc = 0
427};
428
[b45c443]429/* Data and methods for 'zones' command */
[80bff342]430static int cmd_zones(cmd_arg_t *argv);
431static cmd_info_t zones_info = {
432 .name = "zones",
[df58e44]433 .description = "List memory zones.",
[80bff342]434 .func = cmd_zones,
435 .argc = 0
436};
437
[df58e44]438/* Data and methods for 'zone' command */
439static int cmd_zone(cmd_arg_t *argv);
440static cmd_arg_t zone_argv = {
441 .type = ARG_TYPE_INT,
442};
443
444static cmd_info_t zone_info = {
445 .name = "zone",
446 .description = "<zone> Show memory zone structure.",
447 .func = cmd_zone,
448 .argc = 1,
449 .argv = &zone_argv
450};
451
[8a64e81e]452/* Data and methods for the 'workq' command */
453static int cmd_workq(cmd_arg_t *argv);
454static cmd_info_t workq_info = {
455 .name = "workq",
456 .description = "Show global workq information.",
457 .func = cmd_workq,
458 .argc = 0
459};
460
[073c9e6]461/* Data and methods for 'ipc' command */
462static int cmd_ipc(cmd_arg_t *argv);
463static cmd_arg_t ipc_argv = {
[c4e4507]464 .type = ARG_TYPE_INT,
465};
[073c9e6]466static cmd_info_t ipc_info = {
467 .name = "ipc",
[df58e44]468 .description = "<taskid> Show IPC information of a task.",
[073c9e6]469 .func = cmd_ipc,
[c4e4507]470 .argc = 1,
[073c9e6]471 .argv = &ipc_argv
[c4e4507]472};
473
[2a75302]474/* Data and methods for 'kill' command */
475static int cmd_kill(cmd_arg_t *argv);
476static cmd_arg_t kill_argv = {
477 .type = ARG_TYPE_INT,
478};
479static cmd_info_t kill_info = {
480 .name = "kill",
[df58e44]481 .description = "<taskid> Kill a task.",
[2a75302]482 .func = cmd_kill,
483 .argc = 1,
484 .argv = &kill_argv
485};
486
[b45c443]487/* Data and methods for 'cpus' command. */
[0132630]488static int cmd_cpus(cmd_arg_t *argv);
489cmd_info_t cpus_info = {
490 .name = "cpus",
491 .description = "List all processors.",
492 .help = NULL,
493 .func = cmd_cpus,
494 .argc = 0,
495 .argv = NULL
496};
497
[b45c443]498/* Data and methods for 'version' command. */
[0132630]499static int cmd_version(cmd_arg_t *argv);
500cmd_info_t version_info = {
501 .name = "version",
502 .description = "Print version information.",
503 .help = NULL,
504 .func = cmd_version,
505 .argc = 0,
506 .argv = NULL
507};
508
[10e16a7]509static cmd_info_t *basic_commands[] = {
510 &call0_info,
[e5dbbe5]511 &mcall0_info,
[10e16a7]512 &call1_info,
513 &call2_info,
514 &call3_info,
[41d33ac]515 &continue_info,
[10e16a7]516 &cpus_info,
517 &desc_info,
518 &halt_info,
519 &help_info,
[073c9e6]520 &ipc_info,
[2a75302]521 &kill_info,
[df58e44]522 &physmem_info,
523 &reboot_info,
524 &sched_info,
[10e16a7]525 &set4_info,
526 &slabs_info,
527 &symaddr_info,
[df58e44]528 &sysinfo_info,
[37c57f2]529 &tasks_info,
[df58e44]530 &threads_info,
[10e16a7]531 &tlb_info,
[df58e44]532 &uptime_info,
[10e16a7]533 &version_info,
[8a64e81e]534 &workq_info,
[10e16a7]535 &zones_info,
536 &zone_info,
[319e60e]537#ifdef CONFIG_TEST
538 &test_info,
[95155b0c]539 &bench_info,
[5b7a107]540#endif
541#ifdef CONFIG_UDEBUG
542 &btrace_info,
[319e60e]543#endif
[10e16a7]544 NULL
545};
[80bff342]546
547
[442d0ae]548/** Initialize command info structure.
549 *
550 * @param cmd Command info structure.
551 *
552 */
553void cmd_initialize(cmd_info_t *cmd)
554{
[da1bafb]555 spinlock_initialize(&cmd->lock, "cmd.lock");
[442d0ae]556 link_initialize(&cmd->link);
557}
558
559/** Initialize and register commands. */
560void cmd_init(void)
561{
[b07c332]562 unsigned int i;
[80bff342]563
[b07c332]564 for (i = 0; basic_commands[i]; i++) {
[10e16a7]565 cmd_initialize(basic_commands[i]);
[40fb017]566 }
567
568 for (i = 0; basic_commands[i]; i++) {
569 if (!cmd_register(basic_commands[i])) {
570 printf("Cannot register command %s\n",
571 basic_commands[i]->name);
572 }
[10e16a7]573 }
[442d0ae]574}
575
576/** List supported commands.
577 *
578 * @param argv Argument vector.
579 *
580 * @return 0 on failure, 1 on success.
581 */
582int cmd_help(cmd_arg_t *argv)
583{
584 spinlock_lock(&cmd_lock);
585
[98000fb]586 size_t len = 0;
[55b77d9]587 list_foreach(cmd_list, cur) {
[442d0ae]588 cmd_info_t *hlp;
589 hlp = list_get_instance(cur, cmd_info_t, link);
[c1f7f6ea]590
[442d0ae]591 spinlock_lock(&hlp->lock);
[20cc877]592 if (str_length(hlp->name) > len)
593 len = str_length(hlp->name);
[c1f7f6ea]594 spinlock_unlock(&hlp->lock);
595 }
596
[0b0f4bb]597 unsigned int _len = (unsigned int) len;
598 if ((_len != len) || (((int) _len) < 0)) {
599 printf("Command length overflow\n");
600 return 1;
601 }
602
[55b77d9]603 list_foreach(cmd_list, cur) {
[c1f7f6ea]604 cmd_info_t *hlp;
605 hlp = list_get_instance(cur, cmd_info_t, link);
[442d0ae]606
[c1f7f6ea]607 spinlock_lock(&hlp->lock);
[0b0f4bb]608 printf("%-*s %s\n", _len, hlp->name, hlp->description);
[442d0ae]609 spinlock_unlock(&hlp->lock);
610 }
611
[ae318d3]612 spinlock_unlock(&cmd_lock);
[c1f7f6ea]613
[442d0ae]614 return 1;
615}
616
[f74bbaf]617/** Reboot the system.
618 *
619 * @param argv Argument vector.
620 *
621 * @return 0 on failure, 1 on success.
622 */
623int cmd_reboot(cmd_arg_t *argv)
624{
625 reboot();
626
627 /* Not reached */
628 return 1;
629}
630
[4b662f8c]631/** Print system uptime information.
632 *
633 * @param argv Argument vector.
634 *
635 * @return 0 on failure, 1 on success.
636 */
637int cmd_uptime(cmd_arg_t *argv)
638{
639 ASSERT(uptime);
640
641 /* This doesn't have to be very accurate */
[96b02eb9]642 sysarg_t sec = uptime->seconds1;
[4b662f8c]643
[c859753]644 printf("Up %" PRIun " days, %" PRIun " hours, %" PRIun " minutes, %" PRIun " seconds\n",
[4b662f8c]645 sec / 86400, (sec % 86400) / 3600, (sec % 3600) / 60, sec % 60);
646
647 return 1;
648}
649
[442d0ae]650/** Describe specified command.
651 *
652 * @param argv Argument vector.
653 *
654 * @return 0 on failure, 1 on success.
655 */
656int cmd_desc(cmd_arg_t *argv)
657{
658 spinlock_lock(&cmd_lock);
659
[55b77d9]660 list_foreach(cmd_list, cur) {
[442d0ae]661 cmd_info_t *hlp;
662
663 hlp = list_get_instance(cur, cmd_info_t, link);
664 spinlock_lock(&hlp->lock);
[20cc877]665
666 if (str_lcmp(hlp->name, (const char *) argv->buffer, str_length(hlp->name)) == 0) {
[442d0ae]667 printf("%s - %s\n", hlp->name, hlp->description);
668 if (hlp->help)
669 hlp->help();
670 spinlock_unlock(&hlp->lock);
671 break;
672 }
[20cc877]673
[442d0ae]674 spinlock_unlock(&hlp->lock);
675 }
676
677 spinlock_unlock(&cmd_lock);
[20cc877]678
[442d0ae]679 return 1;
680}
681
682/** Search symbol table */
683int cmd_symaddr(cmd_arg_t *argv)
684{
[828aa05]685 symtab_print_search((char *) argv->buffer);
[442d0ae]686
687 return 1;
688}
689
690/** Call function with zero parameters */
691int cmd_call0(cmd_arg_t *argv)
692{
[7f1c620]693 uintptr_t symaddr;
[e16e0d59]694 char *symbol;
[96b02eb9]695 sysarg_t (*fnc)(void);
[0f81ceb7]696 fncptr_t fptr;
[e16e0d59]697 int rc;
[e2b762ec]698
[e16e0d59]699 symbol = (char *) argv->buffer;
700 rc = symtab_addr_lookup(symbol, &symaddr);
701
702 if (rc == ENOENT)
703 printf("Symbol %s not found.\n", symbol);
704 else if (rc == EOVERFLOW) {
705 symtab_print_search(symbol);
[442d0ae]706 printf("Duplicate symbol, be more specific.\n");
[e16e0d59]707 } else if (rc == EOK) {
[c992538a]708 ipl_t ipl;
709
710 ipl = interrupts_disable();
[96b02eb9]711 fnc = (sysarg_t (*)(void)) arch_construct_function(&fptr,
[e16e0d59]712 (void *) symaddr, (void *) cmd_call0);
[0b0f4bb]713 printf("Calling %s() (%p)\n", symbol, (void *) symaddr);
[0f81ceb7]714 printf("Result: %#" PRIxn "\n", fnc());
[c992538a]715 interrupts_restore(ipl);
[e16e0d59]716 } else {
717 printf("No symbol information available.\n");
[442d0ae]718 }
719 return 1;
720}
721
[e5dbbe5]722/** Call function with zero parameters on each CPU */
723int cmd_mcall0(cmd_arg_t *argv)
724{
725 /*
726 * For each CPU, create a thread which will
727 * call the function.
728 */
729
[0b0f4bb]730 unsigned int i;
[e5dbbe5]731 for (i = 0; i < config.cpu_count; i++) {
[b8f11baa]732 if (!cpus[i].active)
733 continue;
734
[da1bafb]735 thread_t *thread;
736 if ((thread = thread_create((void (*)(void *)) cmd_call0,
[6eef3c4]737 (void *) argv, TASK, THREAD_FLAG_NONE, "call0"))) {
[0b0f4bb]738 printf("cpu%u: ", i);
[6eef3c4]739 thread_wire(thread, &cpus[i]);
[da1bafb]740 thread_ready(thread);
741 thread_join(thread);
742 thread_detach(thread);
[e5dbbe5]743 } else
[0b0f4bb]744 printf("Unable to create thread for cpu%u\n", i);
[e5dbbe5]745 }
746
747 return 1;
748}
749
[442d0ae]750/** Call function with one parameter */
751int cmd_call1(cmd_arg_t *argv)
752{
[7f1c620]753 uintptr_t symaddr;
[442d0ae]754 char *symbol;
[96b02eb9]755 sysarg_t (*fnc)(sysarg_t, ...);
756 sysarg_t arg1 = argv[1].intval;
[0f81ceb7]757 fncptr_t fptr;
[e16e0d59]758 int rc;
[e2b762ec]759
[e16e0d59]760 symbol = (char *) argv->buffer;
761 rc = symtab_addr_lookup(symbol, &symaddr);
762
763 if (rc == ENOENT) {
764 printf("Symbol %s not found.\n", symbol);
765 } else if (rc == EOVERFLOW) {
766 symtab_print_search(symbol);
[442d0ae]767 printf("Duplicate symbol, be more specific.\n");
[e16e0d59]768 } else if (rc == EOK) {
[c992538a]769 ipl_t ipl;
770
771 ipl = interrupts_disable();
[96b02eb9]772 fnc = (sysarg_t (*)(sysarg_t, ...))
[0b0f4bb]773 arch_construct_function(&fptr, (void *) symaddr,
774 (void *) cmd_call1);
775 printf("Calling f(%#" PRIxn "): %p: %s\n", arg1,
776 (void *) symaddr, symbol);
[0f81ceb7]777 printf("Result: %#" PRIxn "\n", fnc(arg1));
[c992538a]778 interrupts_restore(ipl);
[e16e0d59]779 } else {
780 printf("No symbol information available.\n");
[442d0ae]781 }
[e16e0d59]782
[442d0ae]783 return 1;
784}
785
786/** Call function with two parameters */
787int cmd_call2(cmd_arg_t *argv)
788{
[7f1c620]789 uintptr_t symaddr;
[442d0ae]790 char *symbol;
[96b02eb9]791 sysarg_t (*fnc)(sysarg_t, sysarg_t, ...);
792 sysarg_t arg1 = argv[1].intval;
793 sysarg_t arg2 = argv[2].intval;
[0f81ceb7]794 fncptr_t fptr;
[e16e0d59]795 int rc;
796
797 symbol = (char *) argv->buffer;
798 rc = symtab_addr_lookup(symbol, &symaddr);
799
800 if (rc == ENOENT) {
801 printf("Symbol %s not found.\n", symbol);
802 } else if (rc == EOVERFLOW) {
803 symtab_print_search(symbol);
[442d0ae]804 printf("Duplicate symbol, be more specific.\n");
[e16e0d59]805 } else if (rc == EOK) {
[c992538a]806 ipl_t ipl;
807
808 ipl = interrupts_disable();
[96b02eb9]809 fnc = (sysarg_t (*)(sysarg_t, sysarg_t, ...))
[0b0f4bb]810 arch_construct_function(&fptr, (void *) symaddr,
811 (void *) cmd_call2);
[c859753]812 printf("Calling f(%#" PRIxn ", %#" PRIxn "): %p: %s\n",
[0b0f4bb]813 arg1, arg2, (void *) symaddr, symbol);
[0f81ceb7]814 printf("Result: %#" PRIxn "\n", fnc(arg1, arg2));
[c992538a]815 interrupts_restore(ipl);
[e16e0d59]816 } else {
817 printf("No symbol information available.\n");
818 }
[442d0ae]819 return 1;
820}
821
822/** Call function with three parameters */
823int cmd_call3(cmd_arg_t *argv)
824{
[7f1c620]825 uintptr_t symaddr;
[442d0ae]826 char *symbol;
[96b02eb9]827 sysarg_t (*fnc)(sysarg_t, sysarg_t, sysarg_t, ...);
828 sysarg_t arg1 = argv[1].intval;
829 sysarg_t arg2 = argv[2].intval;
830 sysarg_t arg3 = argv[3].intval;
[0f81ceb7]831 fncptr_t fptr;
[e16e0d59]832 int rc;
[0f81ceb7]833
[e16e0d59]834 symbol = (char *) argv->buffer;
835 rc = symtab_addr_lookup(symbol, &symaddr);
836
837 if (rc == ENOENT) {
838 printf("Symbol %s not found.\n", symbol);
839 } else if (rc == EOVERFLOW) {
840 symtab_print_search(symbol);
[442d0ae]841 printf("Duplicate symbol, be more specific.\n");
[e16e0d59]842 } else if (rc == EOK) {
[c992538a]843 ipl_t ipl;
844
845 ipl = interrupts_disable();
[96b02eb9]846 fnc = (sysarg_t (*)(sysarg_t, sysarg_t, sysarg_t, ...))
[0b0f4bb]847 arch_construct_function(&fptr, (void *) symaddr,
848 (void *) cmd_call3);
849 printf("Calling f(%#" PRIxn ",%#" PRIxn ", %#" PRIxn "): %p: %s\n",
850 arg1, arg2, arg3, (void *) symaddr, symbol);
[0f81ceb7]851 printf("Result: %#" PRIxn "\n", fnc(arg1, arg2, arg3));
[c992538a]852 interrupts_restore(ipl);
[e16e0d59]853 } else {
854 printf("No symbol information available.\n");
[442d0ae]855 }
856 return 1;
857}
858
859/** Print detailed description of 'describe' command. */
860void desc_help(void)
861{
862 printf("Syntax: describe command_name\n");
863}
864
865/** Halt the kernel.
866 *
867 * @param argv Argument vector (ignored).
868 *
869 * @return 0 on failure, 1 on success (never returns).
870 */
871int cmd_halt(cmd_arg_t *argv)
872{
873 halt();
874 return 1;
875}
876
877/** Command for printing TLB contents.
878 *
879 * @param argv Not used.
880 *
881 * @return Always returns 1.
882 */
[0132630]883int cmd_tlb(cmd_arg_t *argv)
[442d0ae]884{
885 tlb_print();
886 return 1;
887}
[ba276f7]888
[b07c332]889/** Command for printing physical memory configuration.
890 *
891 * @param argv Not used.
892 *
893 * @return Always returns 1.
894 */
895int cmd_physmem(cmd_arg_t *argv)
896{
897 physmem_print();
898 return 1;
899}
900
[ba276f7]901/** Write 4 byte value to address */
902int cmd_set4(cmd_arg_t *argv)
903{
[e16e0d59]904 uintptr_t addr;
[7f1c620]905 uint32_t arg1 = argv[1].intval;
[ba276f7]906 bool pointer = false;
[e16e0d59]907 int rc;
[4ce914d4]908
909 if (((char *) argv->buffer)[0] == '*') {
[e16e0d59]910 rc = symtab_addr_lookup((char *) argv->buffer + 1, &addr);
[ba276f7]911 pointer = true;
[4ce914d4]912 } else if (((char *) argv->buffer)[0] >= '0' &&
913 ((char *) argv->buffer)[0] <= '9') {
914 uint64_t value;
[059a8e4]915 rc = str_uint64_t((char *) argv->buffer, NULL, 0, true, &value);
[4ce914d4]916 if (rc == EOK)
917 addr = (uintptr_t) value;
918 } else
[e16e0d59]919 rc = symtab_addr_lookup((char *) argv->buffer, &addr);
[4ce914d4]920
[e16e0d59]921 if (rc == ENOENT)
[0b0f4bb]922 printf("Symbol %s not found.\n", (char *) argv->buffer);
[4ce914d4]923 else if (rc == EINVAL)
924 printf("Invalid address.\n");
[e16e0d59]925 else if (rc == EOVERFLOW) {
[828aa05]926 symtab_print_search((char *) argv->buffer);
[4ce914d4]927 printf("Duplicate symbol (be more specific) or address overflow.\n");
[e16e0d59]928 } else if (rc == EOK) {
[ba276f7]929 if (pointer)
[e16e0d59]930 addr = *(uintptr_t *) addr;
[0b0f4bb]931 printf("Writing %#" PRIx32" -> %p\n", arg1, (void *) addr);
[e16e0d59]932 *(uint32_t *) addr = arg1;
[4ce914d4]933 } else
[e16e0d59]934 printf("No symbol information available.\n");
[ba276f7]935
936 return 1;
937}
[80bff342]938
[4e147a6]939/** Command for listings SLAB caches
940 *
941 * @param argv Ignores
942 *
943 * @return Always 1
944 */
[df58e44]945int cmd_slabs(cmd_arg_t *argv)
[095b1534]946{
[4e147a6]947 slab_print_list();
948 return 1;
949}
950
[b3631bc]951/** Command for dumping sysinfo
952 *
953 * @param argv Ignores
954 *
955 * @return Always 1
956 */
[df58e44]957int cmd_sysinfo(cmd_arg_t *argv)
[b3631bc]958{
[9dae191e]959 sysinfo_dump(NULL);
[b3631bc]960 return 1;
961}
962
[df58e44]963/** Command for listing thread information
[55ab0f1]964 *
[48dcc69]965 * @param argv Ignored
[55ab0f1]966 *
967 * @return Always 1
968 */
[48dcc69]969int cmd_threads(cmd_arg_t *argv)
[095b1534]970{
[48dcc69]971 if (str_cmp(flag_buf, "-a") == 0)
972 thread_print_list(true);
973 else if (str_cmp(flag_buf, "") == 0)
974 thread_print_list(false);
975 else
976 printf("Unknown argument \"%s\".\n", flag_buf);
977
[55ab0f1]978 return 1;
979}
980
[df58e44]981/** Command for listing task information
[37c57f2]982 *
[48dcc69]983 * @param argv Ignored
[37c57f2]984 *
985 * @return Always 1
986 */
[c0f13d2]987int cmd_tasks(cmd_arg_t *argv)
[095b1534]988{
[48dcc69]989 if (str_cmp(flag_buf, "-a") == 0)
[c0f13d2]990 task_print_list(true);
[48dcc69]991 else if (str_cmp(flag_buf, "") == 0)
[c0f13d2]992 task_print_list(false);
993 else
[48dcc69]994 printf("Unknown argument \"%s\".\n", flag_buf);
[c0f13d2]995
[37c57f2]996 return 1;
997}
998
[5b7a107]999#ifdef CONFIG_UDEBUG
1000
[df58e44]1001/** Command for printing thread stack trace
1002 *
1003 * @param argv Integer argument from cmdline expected
1004 *
1005 * return Always 1
1006 *
1007 */
1008int cmd_btrace(cmd_arg_t *argv)
1009{
1010 thread_stack_trace(argv[0].intval);
1011 return 1;
1012}
1013
[5b7a107]1014#endif /* CONFIG_UDEBUG */
1015
[df58e44]1016/** Command for printing scheduler information
[10e16a7]1017 *
1018 * @param argv Ignores
1019 *
1020 * @return Always 1
1021 */
[df58e44]1022int cmd_sched(cmd_arg_t *argv)
[095b1534]1023{
[10e16a7]1024 sched_print_list();
1025 return 1;
1026}
1027
[8a64e81e]1028/** Prints information about the global work queue.
1029 *
1030 * @param argv Ignores
1031 *
1032 * @return Always 1
1033 */
1034int cmd_workq(cmd_arg_t *argv)
1035{
1036 workq_global_print_info();
1037 return 1;
1038}
1039
1040
[96cacc1]1041/** Command for listing memory zones
1042 *
1043 * @param argv Ignored
1044 *
1045 * return Always 1
1046 */
[df58e44]1047int cmd_zones(cmd_arg_t *argv)
[095b1534]1048{
[9dae191e]1049 zones_print_list();
[80bff342]1050 return 1;
1051}
[0132630]1052
[96cacc1]1053/** Command for memory zone details
1054 *
1055 * @param argv Integer argument from cmdline expected
1056 *
1057 * return Always 1
1058 */
[df58e44]1059int cmd_zone(cmd_arg_t *argv)
[095b1534]1060{
[dfd9186]1061 zone_print_one(argv[0].intval);
[80bff342]1062 return 1;
1063}
1064
[df58e44]1065/** Command for printing task IPC details
[c4e4507]1066 *
1067 * @param argv Integer argument from cmdline expected
1068 *
1069 * return Always 1
1070 */
[df58e44]1071int cmd_ipc(cmd_arg_t *argv)
[095b1534]1072{
[c4e4507]1073 ipc_print_task(argv[0].intval);
1074 return 1;
1075}
1076
[cb3d641a]1077/** Command for killing a task
[2a75302]1078 *
1079 * @param argv Integer argument from cmdline expected
1080 *
[cb3d641a]1081 * return 0 on failure, 1 on success.
[2a75302]1082 */
[df58e44]1083int cmd_kill(cmd_arg_t *argv)
[2a75302]1084{
1085 if (task_kill(argv[0].intval) != EOK)
1086 return 0;
1087
1088 return 1;
1089}
[c4e4507]1090
[0132630]1091/** Command for listing processors.
1092 *
1093 * @param argv Ignored.
1094 *
1095 * return Always 1.
1096 */
1097int cmd_cpus(cmd_arg_t *argv)
1098{
1099 cpu_list();
1100 return 1;
1101}
1102
1103/** Command for printing kernel version.
1104 *
1105 * @param argv Ignored.
1106 *
1107 * return Always 1.
1108 */
1109int cmd_version(cmd_arg_t *argv)
1110{
1111 version_print();
1112 return 1;
1113}
[41d33ac]1114
1115/** Command for returning console back to userspace.
1116 *
1117 * @param argv Ignored.
1118 *
1119 * return Always 1.
1120 */
1121int cmd_continue(cmd_arg_t *argv)
1122{
[dd054bc2]1123 printf("The kernel will now relinquish the console.\n");
[516ff92]1124 release_console();
[3ad953c]1125
[f9061b4]1126 event_notify_0(EVENT_KCONSOLE, false);
[821cc93]1127 indev_pop_character(stdin);
[3ad953c]1128
[41d33ac]1129 return 1;
1130}
[b45c443]1131
[50661ab]1132#ifdef CONFIG_TEST
[62b6d17]1133static bool run_test(const test_t *test)
[34db7fa]1134{
[c1f7f6ea]1135 printf("%s (%s)\n", test->name, test->desc);
[cce6acf]1136
1137 /* Update and read thread accounting
1138 for benchmarking */
[da1bafb]1139 irq_spinlock_lock(&TASK->lock, true);
[1ba37fa]1140 uint64_t ucycles0, kcycles0;
1141 task_get_accounting(TASK, &ucycles0, &kcycles0);
[da1bafb]1142 irq_spinlock_unlock(&TASK->lock, true);
[cce6acf]1143
1144 /* Execute the test */
[cb01e1e]1145 test_quiet = false;
[a000878c]1146 const char *ret = test->entry();
[cce6acf]1147
1148 /* Update and read thread accounting */
[da1bafb]1149 uint64_t ucycles1, kcycles1;
1150 irq_spinlock_lock(&TASK->lock, true);
[1ba37fa]1151 task_get_accounting(TASK, &ucycles1, &kcycles1);
[da1bafb]1152 irq_spinlock_unlock(&TASK->lock, true);
[cce6acf]1153
[1ba37fa]1154 uint64_t ucycles, kcycles;
1155 char usuffix, ksuffix;
[e535eeb]1156 order_suffix(ucycles1 - ucycles0, &ucycles, &usuffix);
1157 order_suffix(kcycles1 - kcycles0, &kcycles, &ksuffix);
[da1bafb]1158
[1ba37fa]1159 printf("Time: %" PRIu64 "%c user cycles, %" PRIu64 "%c kernel cycles\n",
[da1bafb]1160 ucycles, usuffix, kcycles, ksuffix);
[34db7fa]1161
1162 if (ret == NULL) {
1163 printf("Test passed\n");
[62b6d17]1164 return true;
[34db7fa]1165 }
[da1bafb]1166
[34db7fa]1167 printf("%s\n", ret);
[62b6d17]1168 return false;
[34db7fa]1169}
1170
[95155b0c]1171static bool run_bench(const test_t *test, const uint32_t cnt)
1172{
1173 uint32_t i;
1174 bool ret = true;
[1ba37fa]1175 uint64_t ucycles, kcycles;
1176 char usuffix, ksuffix;
[95155b0c]1177
1178 if (cnt < 1)
1179 return true;
1180
[828aa05]1181 uint64_t *data = (uint64_t *) malloc(sizeof(uint64_t) * cnt, 0);
[95155b0c]1182 if (data == NULL) {
1183 printf("Error allocating memory for statistics\n");
1184 return false;
1185 }
1186
1187 for (i = 0; i < cnt; i++) {
[c859753]1188 printf("%s (%u/%u) ... ", test->name, i + 1, cnt);
[95155b0c]1189
1190 /* Update and read thread accounting
1191 for benchmarking */
[da1bafb]1192 irq_spinlock_lock(&TASK->lock, true);
[1ba37fa]1193 uint64_t ucycles0, kcycles0;
1194 task_get_accounting(TASK, &ucycles0, &kcycles0);
[da1bafb]1195 irq_spinlock_unlock(&TASK->lock, true);
[95155b0c]1196
1197 /* Execute the test */
[cb01e1e]1198 test_quiet = true;
[21881bd8]1199 const char *test_ret = test->entry();
[95155b0c]1200
1201 /* Update and read thread accounting */
[da1bafb]1202 irq_spinlock_lock(&TASK->lock, true);
[1ba37fa]1203 uint64_t ucycles1, kcycles1;
1204 task_get_accounting(TASK, &ucycles1, &kcycles1);
[da1bafb]1205 irq_spinlock_unlock(&TASK->lock, true);
1206
[21881bd8]1207 if (test_ret != NULL) {
1208 printf("%s\n", test_ret);
[95155b0c]1209 ret = false;
1210 break;
1211 }
1212
[1ba37fa]1213 data[i] = ucycles1 - ucycles0 + kcycles1 - kcycles0;
[e535eeb]1214 order_suffix(ucycles1 - ucycles0, &ucycles, &usuffix);
1215 order_suffix(kcycles1 - kcycles0, &kcycles, &ksuffix);
[1ba37fa]1216 printf("OK (%" PRIu64 "%c user cycles, %" PRIu64 "%c kernel cycles)\n",
[da1bafb]1217 ucycles, usuffix, kcycles, ksuffix);
[95155b0c]1218 }
1219
1220 if (ret) {
1221 printf("\n");
1222
1223 uint64_t sum = 0;
1224
1225 for (i = 0; i < cnt; i++) {
1226 sum += data[i];
1227 }
1228
[e535eeb]1229 order_suffix(sum / (uint64_t) cnt, &ucycles, &usuffix);
[1ba37fa]1230 printf("Average\t\t%" PRIu64 "%c\n", ucycles, usuffix);
[95155b0c]1231 }
1232
1233 free(data);
1234
1235 return ret;
1236}
1237
[851f33a]1238static void list_tests(void)
1239{
1240 size_t len = 0;
1241 test_t *test;
1242
1243 for (test = tests; test->name != NULL; test++) {
1244 if (str_length(test->name) > len)
1245 len = str_length(test->name);
1246 }
1247
[0b0f4bb]1248 unsigned int _len = (unsigned int) len;
1249 if ((_len != len) || (((int) _len) < 0)) {
1250 printf("Command length overflow\n");
1251 return;
1252 }
1253
[851f33a]1254 for (test = tests; test->name != NULL; test++)
[0b0f4bb]1255 printf("%-*s %s%s\n", _len, test->name, test->desc,
1256 (test->safe ? "" : " (unsafe)"));
[851f33a]1257
[0b0f4bb]1258 printf("%-*s Run all safe tests\n", _len, "*");
[851f33a]1259}
1260
1261/** Command for listing and running kernel tests
[319e60e]1262 *
1263 * @param argv Argument vector.
1264 *
1265 * return Always 1.
[851f33a]1266 *
[319e60e]1267 */
1268int cmd_test(cmd_arg_t *argv)
1269{
1270 test_t *test;
1271
[20cc877]1272 if (str_cmp((char *) argv->buffer, "*") == 0) {
[50661ab]1273 for (test = tests; test->name != NULL; test++) {
1274 if (test->safe) {
[34db7fa]1275 printf("\n");
1276 if (!run_test(test))
1277 break;
[50661ab]1278 }
1279 }
[851f33a]1280 } else if (str_cmp((char *) argv->buffer, "") != 0) {
[50661ab]1281 bool fnd = false;
1282
1283 for (test = tests; test->name != NULL; test++) {
[20cc877]1284 if (str_cmp(test->name, (char *) argv->buffer) == 0) {
[50661ab]1285 fnd = true;
[34db7fa]1286 run_test(test);
[50661ab]1287 break;
1288 }
[319e60e]1289 }
[50661ab]1290
1291 if (!fnd)
[34db7fa]1292 printf("Unknown test\n");
[851f33a]1293 } else
1294 list_tests();
[319e60e]1295
1296 return 1;
1297}
[95155b0c]1298
1299/** Command for returning kernel tests as benchmarks
1300 *
1301 * @param argv Argument vector.
1302 *
1303 * return Always 1.
1304 */
1305int cmd_bench(cmd_arg_t *argv)
1306{
1307 test_t *test;
1308 uint32_t cnt = argv[1].intval;
1309
[3f2177e]1310 if (str_cmp((char *) argv->buffer, "*") == 0) {
1311 for (test = tests; test->name != NULL; test++) {
1312 if (test->safe) {
1313 if (!run_bench(test, cnt))
1314 break;
1315 }
[95155b0c]1316 }
[3f2177e]1317 } else {
1318 bool fnd = false;
[95155b0c]1319
[3f2177e]1320 for (test = tests; test->name != NULL; test++) {
1321 if (str_cmp(test->name, (char *) argv->buffer) == 0) {
1322 fnd = true;
1323
1324 if (test->safe)
1325 run_bench(test, cnt);
1326 else
1327 printf("Unsafe test\n");
1328
1329 break;
1330 }
1331 }
1332
1333 if (!fnd)
1334 printf("Unknown test\n");
1335 }
[cb01e1e]1336
[95155b0c]1337 return 1;
1338}
1339
[50661ab]1340#endif
[319e60e]1341
[06e1e95]1342/** @}
[b45c443]1343 */
Note: See TracBrowser for help on using the repository browser.