Subversion Repositories HelenOS

Compare Revisions

Ignore whitespace Rev 4490 → Rev 4687

/tags/0.4.1/kernel/generic/src/console/console.c
0,0 → 1,301
/*
* Copyright (c) 2003 Josef Cejka
* Copyright (c) 2005 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup genericconsole
* @{
*/
/** @file
*/
 
#include <console/console.h>
#include <console/chardev.h>
#include <sysinfo/sysinfo.h>
#include <synch/waitq.h>
#include <synch/spinlock.h>
#include <arch/types.h>
#include <ddi/irq.h>
#include <ddi/ddi.h>
#include <ipc/event.h>
#include <ipc/irq.h>
#include <arch.h>
#include <print.h>
#include <putchar.h>
#include <atomic.h>
#include <syscall/copy.h>
#include <errno.h>
#include <string.h>
 
#define KLOG_PAGES 4
#define KLOG_LENGTH (KLOG_PAGES * PAGE_SIZE / sizeof(wchar_t))
#define KLOG_LATENCY 8
 
/** Kernel log cyclic buffer */
static wchar_t klog[KLOG_LENGTH] __attribute__ ((aligned (PAGE_SIZE)));
 
/** Kernel log initialized */
static bool klog_inited = false;
/** First kernel log characters */
static size_t klog_start = 0;
/** Number of valid kernel log characters */
static size_t klog_len = 0;
/** Number of stored (not printed) kernel log characters */
static size_t klog_stored = 0;
/** Number of stored kernel log characters for uspace */
static size_t klog_uspace = 0;
 
/** Kernel log spinlock */
SPINLOCK_INITIALIZE(klog_lock);
 
/** Physical memory area used for klog buffer */
static parea_t klog_parea;
 
static indev_operations_t stdin_ops = {
.poll = NULL
};
 
/** Silence output */
bool silent = false;
 
/** Standard input and output character devices */
indev_t *stdin = NULL;
outdev_t *stdout = NULL;
 
indev_t *stdin_wire(void)
{
if (stdin == NULL) {
stdin = malloc(sizeof(indev_t), FRAME_ATOMIC);
if (stdin != NULL)
indev_initialize("stdin", stdin, &stdin_ops);
}
return stdin;
}
 
/** Initialize kernel logging facility
*
* The shared area contains kernel cyclic buffer. Userspace application may
* be notified on new data with indication of position and size
* of the data within the circular buffer.
*
*/
void klog_init(void)
{
void *faddr = (void *) KA2PA(klog);
ASSERT((uintptr_t) faddr % FRAME_SIZE == 0);
klog_parea.pbase = (uintptr_t) faddr;
klog_parea.frames = SIZE2FRAMES(sizeof(klog));
ddi_parea_register(&klog_parea);
sysinfo_set_item_val("klog.faddr", NULL, (unative_t) faddr);
sysinfo_set_item_val("klog.pages", NULL, KLOG_PAGES);
spinlock_lock(&klog_lock);
klog_inited = true;
spinlock_unlock(&klog_lock);
}
 
void grab_console(void)
{
bool prev = silent;
silent = false;
arch_grab_console();
/* Force the console to print the prompt */
if ((stdin) && (prev))
indev_push_character(stdin, '\n');
}
 
void release_console(void)
{
silent = true;
arch_release_console();
}
 
/** Tell kernel to get keyboard/console access again */
unative_t sys_debug_enable_console(void)
{
#ifdef CONFIG_KCONSOLE
grab_console();
return true;
#else
return false;
#endif
}
 
/** Tell kernel to relinquish keyboard/console access */
unative_t sys_debug_disable_console(void)
{
release_console();
return true;
}
 
/** Get string from input character device.
*
* Read characters from input character device until first occurrence
* of newline character.
*
* @param indev Input character device.
* @param buf Buffer where to store string terminated by NULL.
* @param buflen Size of the buffer.
*
* @return Number of characters read.
*
*/
size_t gets(indev_t *indev, char *buf, size_t buflen)
{
size_t offset = 0;
size_t count = 0;
buf[offset] = 0;
wchar_t ch;
while ((ch = indev_pop_character(indev)) != '\n') {
if (ch == '\b') {
if (count > 0) {
/* Space, backspace, space */
putchar('\b');
putchar(' ');
putchar('\b');
count--;
offset = str_lsize(buf, count);
buf[offset] = 0;
}
}
if (chr_encode(ch, buf, &offset, buflen - 1) == EOK) {
putchar(ch);
count++;
buf[offset] = 0;
}
}
return count;
}
 
/** Get character from input device & echo it to screen */
wchar_t getc(indev_t *indev)
{
wchar_t ch = indev_pop_character(indev);
putchar(ch);
return ch;
}
 
void klog_update(void)
{
spinlock_lock(&klog_lock);
if ((klog_inited) && (event_is_subscribed(EVENT_KLOG)) && (klog_uspace > 0)) {
event_notify_3(EVENT_KLOG, klog_start, klog_len, klog_uspace);
klog_uspace = 0;
}
spinlock_unlock(&klog_lock);
}
 
void putchar(const wchar_t ch)
{
spinlock_lock(&klog_lock);
if ((klog_stored > 0) && (stdout) && (stdout->op->write)) {
/* Print charaters stored in kernel log */
size_t i;
for (i = klog_len - klog_stored; i < klog_len; i++)
stdout->op->write(stdout, klog[(klog_start + i) % KLOG_LENGTH], silent);
klog_stored = 0;
}
/* Store character in the cyclic kernel log */
klog[(klog_start + klog_len) % KLOG_LENGTH] = ch;
if (klog_len < KLOG_LENGTH)
klog_len++;
else
klog_start = (klog_start + 1) % KLOG_LENGTH;
if ((stdout) && (stdout->op->write))
stdout->op->write(stdout, ch, silent);
else {
/* The character is just in the kernel log */
if (klog_stored < klog_len)
klog_stored++;
}
/* The character is stored for uspace */
if (klog_uspace < klog_len)
klog_uspace++;
/* Check notify uspace to update */
bool update;
if ((klog_uspace > KLOG_LATENCY) || (ch == '\n'))
update = true;
else
update = false;
spinlock_unlock(&klog_lock);
if (update)
klog_update();
}
 
/** Print using kernel facility
*
* Print to kernel log.
*
*/
unative_t sys_klog(int fd, const void *buf, size_t size)
{
char *data;
int rc;
if (size > PAGE_SIZE)
return ELIMIT;
if (size > 0) {
data = (char *) malloc(size + 1, 0);
if (!data)
return ENOMEM;
rc = copy_from_uspace(data, buf, size);
if (rc) {
free(data);
return rc;
}
data[size] = 0;
printf("%s", data);
free(data);
} else
klog_update();
return size;
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/console/cmd.c
0,0 → 1,1172
/*
* Copyright (c) 2005 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup genericconsole
* @{
*/
 
/**
* @file cmd.c
* @brief Kernel console command wrappers.
*
* This file is meant to contain all wrapper functions for
* all kconsole commands. The point is in separating
* kconsole specific wrappers from kconsole-unaware functions
* from other subsystems.
*/
 
#include <console/cmd.h>
#include <console/console.h>
#include <console/kconsole.h>
#include <print.h>
#include <panic.h>
#include <arch/types.h>
#include <adt/list.h>
#include <arch.h>
#include <config.h>
#include <func.h>
#include <string.h>
#include <macros.h>
#include <debug.h>
#include <cpu.h>
#include <mm/tlb.h>
#include <arch/mm/tlb.h>
#include <mm/frame.h>
#include <main/version.h>
#include <mm/slab.h>
#include <proc/scheduler.h>
#include <proc/thread.h>
#include <proc/task.h>
#include <ipc/ipc.h>
#include <ipc/irq.h>
#include <ipc/event.h>
#include <symtab.h>
#include <errno.h>
 
#ifdef CONFIG_TEST
#include <test.h>
#endif
 
/* Data and methods for 'help' command. */
static int cmd_help(cmd_arg_t *argv);
static cmd_info_t help_info = {
.name = "help",
.description = "List of supported commands.",
.func = cmd_help,
.argc = 0
};
 
static int cmd_reboot(cmd_arg_t *argv);
static cmd_info_t reboot_info = {
.name = "reboot",
.description = "Reboot.",
.func = cmd_reboot,
.argc = 0
};
 
static int cmd_uptime(cmd_arg_t *argv);
static cmd_info_t uptime_info = {
.name = "uptime",
.description = "Print uptime information.",
.func = cmd_uptime,
.argc = 0
};
 
static int cmd_continue(cmd_arg_t *argv);
static cmd_info_t continue_info = {
.name = "continue",
.description = "Return console back to userspace.",
.func = cmd_continue,
.argc = 0
};
 
#ifdef CONFIG_TEST
static int cmd_tests(cmd_arg_t *argv);
static cmd_info_t tests_info = {
.name = "tests",
.description = "Print available kernel tests.",
.func = cmd_tests,
.argc = 0
};
 
static char test_buf[MAX_CMDLINE + 1];
static int cmd_test(cmd_arg_t *argv);
static cmd_arg_t test_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = test_buf,
.len = sizeof(test_buf)
}
};
static cmd_info_t test_info = {
.name = "test",
.description = "Run kernel test.",
.func = cmd_test,
.argc = 1,
.argv = test_argv
};
 
static int cmd_bench(cmd_arg_t *argv);
static cmd_arg_t bench_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = test_buf,
.len = sizeof(test_buf)
},
{
.type = ARG_TYPE_INT,
}
};
static cmd_info_t bench_info = {
.name = "bench",
.description = "Run kernel test as benchmark.",
.func = cmd_bench,
.argc = 2,
.argv = bench_argv
};
#endif
 
/* Data and methods for 'description' command. */
static int cmd_desc(cmd_arg_t *argv);
static void desc_help(void);
static char desc_buf[MAX_CMDLINE+1];
static cmd_arg_t desc_argv = {
.type = ARG_TYPE_STRING,
.buffer = desc_buf,
.len = sizeof(desc_buf)
};
static cmd_info_t desc_info = {
.name = "describe",
.description = "Describe specified command.",
.help = desc_help,
.func = cmd_desc,
.argc = 1,
.argv = &desc_argv
};
 
/* Data and methods for 'symaddr' command. */
static int cmd_symaddr(cmd_arg_t *argv);
static char symaddr_buf[MAX_CMDLINE+1];
static cmd_arg_t symaddr_argv = {
.type = ARG_TYPE_STRING,
.buffer = symaddr_buf,
.len = sizeof(symaddr_buf)
};
static cmd_info_t symaddr_info = {
.name = "symaddr",
.description = "Return symbol address.",
.func = cmd_symaddr,
.argc = 1,
.argv = &symaddr_argv
};
 
static char set_buf[MAX_CMDLINE+1];
static int cmd_set4(cmd_arg_t *argv);
static cmd_arg_t set4_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = set_buf,
.len = sizeof(set_buf)
},
{
.type = ARG_TYPE_INT
}
};
static cmd_info_t set4_info = {
.name = "set4",
.description = "set <dest_addr> <value> - 4byte version",
.func = cmd_set4,
.argc = 2,
.argv = set4_argv
};
 
/* Data and methods for 'call0' command. */
static char call0_buf[MAX_CMDLINE + 1];
static char carg1_buf[MAX_CMDLINE + 1];
static char carg2_buf[MAX_CMDLINE + 1];
static char carg3_buf[MAX_CMDLINE + 1];
 
static int cmd_call0(cmd_arg_t *argv);
static cmd_arg_t call0_argv = {
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
};
static cmd_info_t call0_info = {
.name = "call0",
.description = "call0 <function> -> call function().",
.func = cmd_call0,
.argc = 1,
.argv = &call0_argv
};
 
/* Data and methods for 'mcall0' command. */
static int cmd_mcall0(cmd_arg_t *argv);
static cmd_arg_t mcall0_argv = {
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
};
static cmd_info_t mcall0_info = {
.name = "mcall0",
.description = "mcall0 <function> -> call function() on each CPU.",
.func = cmd_mcall0,
.argc = 1,
.argv = &mcall0_argv
};
 
/* Data and methods for 'call1' command. */
static int cmd_call1(cmd_arg_t *argv);
static cmd_arg_t call1_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg1_buf,
.len = sizeof(carg1_buf)
}
};
static cmd_info_t call1_info = {
.name = "call1",
.description = "call1 <function> <arg1> -> call function(arg1).",
.func = cmd_call1,
.argc = 2,
.argv = call1_argv
};
 
/* Data and methods for 'call2' command. */
static int cmd_call2(cmd_arg_t *argv);
static cmd_arg_t call2_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg1_buf,
.len = sizeof(carg1_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg2_buf,
.len = sizeof(carg2_buf)
}
};
static cmd_info_t call2_info = {
.name = "call2",
.description = "call2 <function> <arg1> <arg2> -> call function(arg1,arg2).",
.func = cmd_call2,
.argc = 3,
.argv = call2_argv
};
 
/* Data and methods for 'call3' command. */
static int cmd_call3(cmd_arg_t *argv);
static cmd_arg_t call3_argv[] = {
{
.type = ARG_TYPE_STRING,
.buffer = call0_buf,
.len = sizeof(call0_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg1_buf,
.len = sizeof(carg1_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg2_buf,
.len = sizeof(carg2_buf)
},
{
.type = ARG_TYPE_VAR,
.buffer = carg3_buf,
.len = sizeof(carg3_buf)
}
 
};
static cmd_info_t call3_info = {
.name = "call3",
.description = "call3 <function> <arg1> <arg2> <arg3> -> call function(arg1,arg2,arg3).",
.func = cmd_call3,
.argc = 4,
.argv = call3_argv
};
 
/* Data and methods for 'halt' command. */
static int cmd_halt(cmd_arg_t *argv);
static cmd_info_t halt_info = {
.name = "halt",
.description = "Halt the kernel.",
.func = cmd_halt,
.argc = 0
};
 
/* Data and methods for 'physmem' command. */
static int cmd_physmem(cmd_arg_t *argv);
cmd_info_t physmem_info = {
.name = "physmem",
.description = "Print physical memory configuration.",
.help = NULL,
.func = cmd_physmem,
.argc = 0,
.argv = NULL
};
 
/* Data and methods for 'tlb' command. */
static int cmd_tlb(cmd_arg_t *argv);
cmd_info_t tlb_info = {
.name = "tlb",
.description = "Print TLB of current processor.",
.help = NULL,
.func = cmd_tlb,
.argc = 0,
.argv = NULL
};
 
static int cmd_threads(cmd_arg_t *argv);
static cmd_info_t threads_info = {
.name = "threads",
.description = "List all threads.",
.func = cmd_threads,
.argc = 0
};
 
static int cmd_tasks(cmd_arg_t *argv);
static cmd_info_t tasks_info = {
.name = "tasks",
.description = "List all tasks.",
.func = cmd_tasks,
.argc = 0
};
 
 
static int cmd_sched(cmd_arg_t *argv);
static cmd_info_t sched_info = {
.name = "scheduler",
.description = "List all scheduler information.",
.func = cmd_sched,
.argc = 0
};
 
static int cmd_slabs(cmd_arg_t *argv);
static cmd_info_t slabs_info = {
.name = "slabs",
.description = "List slab caches.",
.func = cmd_slabs,
.argc = 0
};
 
/* Data and methods for 'zones' command */
static int cmd_zones(cmd_arg_t *argv);
static cmd_info_t zones_info = {
.name = "zones",
.description = "List of memory zones.",
.func = cmd_zones,
.argc = 0
};
 
/* Data and methods for 'ipc' command */
static int cmd_ipc(cmd_arg_t *argv);
static cmd_arg_t ipc_argv = {
.type = ARG_TYPE_INT,
};
static cmd_info_t ipc_info = {
.name = "ipc",
.description = "ipc <taskid> Show IPC information of given task.",
.func = cmd_ipc,
.argc = 1,
.argv = &ipc_argv
};
 
/* Data and methods for 'zone' command */
static int cmd_zone(cmd_arg_t *argv);
static cmd_arg_t zone_argv = {
.type = ARG_TYPE_INT,
};
 
static cmd_info_t zone_info = {
.name = "zone",
.description = "Show memory zone structure.",
.func = cmd_zone,
.argc = 1,
.argv = &zone_argv
};
 
/* Data and methods for 'cpus' command. */
static int cmd_cpus(cmd_arg_t *argv);
cmd_info_t cpus_info = {
.name = "cpus",
.description = "List all processors.",
.help = NULL,
.func = cmd_cpus,
.argc = 0,
.argv = NULL
};
 
/* Data and methods for 'version' command. */
static int cmd_version(cmd_arg_t *argv);
cmd_info_t version_info = {
.name = "version",
.description = "Print version information.",
.help = NULL,
.func = cmd_version,
.argc = 0,
.argv = NULL
};
 
static cmd_info_t *basic_commands[] = {
&call0_info,
&mcall0_info,
&call1_info,
&call2_info,
&call3_info,
&continue_info,
&cpus_info,
&desc_info,
&reboot_info,
&uptime_info,
&halt_info,
&help_info,
&ipc_info,
&set4_info,
&slabs_info,
&symaddr_info,
&sched_info,
&threads_info,
&tasks_info,
&physmem_info,
&tlb_info,
&version_info,
&zones_info,
&zone_info,
#ifdef CONFIG_TEST
&tests_info,
&test_info,
&bench_info,
#endif
NULL
};
 
 
/** Initialize command info structure.
*
* @param cmd Command info structure.
*
*/
void cmd_initialize(cmd_info_t *cmd)
{
spinlock_initialize(&cmd->lock, "cmd");
link_initialize(&cmd->link);
}
 
/** Initialize and register commands. */
void cmd_init(void)
{
unsigned int i;
 
for (i = 0; basic_commands[i]; i++) {
cmd_initialize(basic_commands[i]);
if (!cmd_register(basic_commands[i]))
printf("Cannot register command %s\n", basic_commands[i]->name);
}
}
 
 
/** List supported commands.
*
* @param argv Argument vector.
*
* @return 0 on failure, 1 on success.
*/
int cmd_help(cmd_arg_t *argv)
{
spinlock_lock(&cmd_lock);
link_t *cur;
size_t len = 0;
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp;
hlp = list_get_instance(cur, cmd_info_t, link);
spinlock_lock(&hlp->lock);
if (str_length(hlp->name) > len)
len = str_length(hlp->name);
spinlock_unlock(&hlp->lock);
}
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp;
hlp = list_get_instance(cur, cmd_info_t, link);
spinlock_lock(&hlp->lock);
printf("%-*s %s\n", len, hlp->name, hlp->description);
spinlock_unlock(&hlp->lock);
}
spinlock_unlock(&cmd_lock);
return 1;
}
 
 
/** Reboot the system.
*
* @param argv Argument vector.
*
* @return 0 on failure, 1 on success.
*/
int cmd_reboot(cmd_arg_t *argv)
{
reboot();
/* Not reached */
return 1;
}
 
 
/** Print system uptime information.
*
* @param argv Argument vector.
*
* @return 0 on failure, 1 on success.
*/
int cmd_uptime(cmd_arg_t *argv)
{
ASSERT(uptime);
/* This doesn't have to be very accurate */
unative_t sec = uptime->seconds1;
printf("Up %" PRIun " days, %" PRIun " hours, %" PRIun " minutes, %" PRIun " seconds\n",
sec / 86400, (sec % 86400) / 3600, (sec % 3600) / 60, sec % 60);
return 1;
}
 
/** Describe specified command.
*
* @param argv Argument vector.
*
* @return 0 on failure, 1 on success.
*/
int cmd_desc(cmd_arg_t *argv)
{
link_t *cur;
spinlock_lock(&cmd_lock);
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp;
hlp = list_get_instance(cur, cmd_info_t, link);
spinlock_lock(&hlp->lock);
if (str_lcmp(hlp->name, (const char *) argv->buffer, str_length(hlp->name)) == 0) {
printf("%s - %s\n", hlp->name, hlp->description);
if (hlp->help)
hlp->help();
spinlock_unlock(&hlp->lock);
break;
}
spinlock_unlock(&hlp->lock);
}
spinlock_unlock(&cmd_lock);
return 1;
}
 
/** Search symbol table */
int cmd_symaddr(cmd_arg_t *argv)
{
symtab_print_search((char *) argv->buffer);
return 1;
}
 
/** Call function with zero parameters */
int cmd_call0(cmd_arg_t *argv)
{
uintptr_t symaddr;
char *symbol;
unative_t (*fnc)(void);
fncptr_t fptr;
int rc;
 
symbol = (char *) argv->buffer;
rc = symtab_addr_lookup(symbol, &symaddr);
 
if (rc == ENOENT)
printf("Symbol %s not found.\n", symbol);
else if (rc == EOVERFLOW) {
symtab_print_search(symbol);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
fnc = (unative_t (*)(void)) arch_construct_function(&fptr,
(void *) symaddr, (void *) cmd_call0);
printf("Calling %s() (%p)\n", symbol, symaddr);
printf("Result: %#" PRIxn "\n", fnc());
} else {
printf("No symbol information available.\n");
}
return 1;
}
 
/** Call function with zero parameters on each CPU */
int cmd_mcall0(cmd_arg_t *argv)
{
/*
* For each CPU, create a thread which will
* call the function.
*/
size_t i;
for (i = 0; i < config.cpu_count; i++) {
if (!cpus[i].active)
continue;
thread_t *t;
if ((t = thread_create((void (*)(void *)) cmd_call0, (void *) argv, TASK, THREAD_FLAG_WIRED, "call0", false))) {
spinlock_lock(&t->lock);
t->cpu = &cpus[i];
spinlock_unlock(&t->lock);
printf("cpu%u: ", i);
thread_ready(t);
thread_join(t);
thread_detach(t);
} else
printf("Unable to create thread for cpu%u\n", i);
}
return 1;
}
 
/** Call function with one parameter */
int cmd_call1(cmd_arg_t *argv)
{
uintptr_t symaddr;
char *symbol;
unative_t (*fnc)(unative_t, ...);
unative_t arg1 = argv[1].intval;
fncptr_t fptr;
int rc;
 
symbol = (char *) argv->buffer;
rc = symtab_addr_lookup(symbol, &symaddr);
 
if (rc == ENOENT) {
printf("Symbol %s not found.\n", symbol);
} else if (rc == EOVERFLOW) {
symtab_print_search(symbol);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
fnc = (unative_t (*)(unative_t, ...)) arch_construct_function(&fptr, (void *) symaddr, (void *) cmd_call1);
printf("Calling f(%#" PRIxn "): %p: %s\n", arg1, symaddr, symbol);
printf("Result: %#" PRIxn "\n", fnc(arg1));
} else {
printf("No symbol information available.\n");
}
 
return 1;
}
 
/** Call function with two parameters */
int cmd_call2(cmd_arg_t *argv)
{
uintptr_t symaddr;
char *symbol;
unative_t (*fnc)(unative_t, unative_t, ...);
unative_t arg1 = argv[1].intval;
unative_t arg2 = argv[2].intval;
fncptr_t fptr;
int rc;
 
symbol = (char *) argv->buffer;
rc = symtab_addr_lookup(symbol, &symaddr);
 
if (rc == ENOENT) {
printf("Symbol %s not found.\n", symbol);
} else if (rc == EOVERFLOW) {
symtab_print_search(symbol);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
fnc = (unative_t (*)(unative_t, unative_t, ...)) arch_construct_function(&fptr, (void *) symaddr, (void *) cmd_call2);
printf("Calling f(%#" PRIxn ", %#" PRIxn "): %p: %s\n",
arg1, arg2, symaddr, symbol);
printf("Result: %#" PRIxn "\n", fnc(arg1, arg2));
} else {
printf("No symbol information available.\n");
}
return 1;
}
 
/** Call function with three parameters */
int cmd_call3(cmd_arg_t *argv)
{
uintptr_t symaddr;
char *symbol;
unative_t (*fnc)(unative_t, unative_t, unative_t, ...);
unative_t arg1 = argv[1].intval;
unative_t arg2 = argv[2].intval;
unative_t arg3 = argv[3].intval;
fncptr_t fptr;
int rc;
symbol = (char *) argv->buffer;
rc = symtab_addr_lookup(symbol, &symaddr);
 
if (rc == ENOENT) {
printf("Symbol %s not found.\n", symbol);
} else if (rc == EOVERFLOW) {
symtab_print_search(symbol);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
fnc = (unative_t (*)(unative_t, unative_t, unative_t, ...)) arch_construct_function(&fptr, (void *) symaddr, (void *) cmd_call3);
printf("Calling f(%#" PRIxn ",%#" PRIxn ", %#" PRIxn "): %p: %s\n",
arg1, arg2, arg3, symaddr, symbol);
printf("Result: %#" PRIxn "\n", fnc(arg1, arg2, arg3));
} else {
printf("No symbol information available.\n");
}
return 1;
}
 
 
/** Print detailed description of 'describe' command. */
void desc_help(void)
{
printf("Syntax: describe command_name\n");
}
 
/** Halt the kernel.
*
* @param argv Argument vector (ignored).
*
* @return 0 on failure, 1 on success (never returns).
*/
int cmd_halt(cmd_arg_t *argv)
{
halt();
return 1;
}
 
/** Command for printing TLB contents.
*
* @param argv Not used.
*
* @return Always returns 1.
*/
int cmd_tlb(cmd_arg_t *argv)
{
tlb_print();
return 1;
}
 
/** Command for printing physical memory configuration.
*
* @param argv Not used.
*
* @return Always returns 1.
*/
int cmd_physmem(cmd_arg_t *argv)
{
physmem_print();
return 1;
}
 
/** Write 4 byte value to address */
int cmd_set4(cmd_arg_t *argv)
{
uintptr_t addr;
uint32_t arg1 = argv[1].intval;
bool pointer = false;
int rc;
 
if (((char *)argv->buffer)[0] == '*') {
rc = symtab_addr_lookup((char *) argv->buffer + 1, &addr);
pointer = true;
} else if (((char *) argv->buffer)[0] >= '0' &&
((char *)argv->buffer)[0] <= '9') {
rc = EOK;
addr = atoi((char *)argv->buffer);
} else {
rc = symtab_addr_lookup((char *) argv->buffer, &addr);
}
 
if (rc == ENOENT)
printf("Symbol %s not found.\n", argv->buffer);
else if (rc == EOVERFLOW) {
symtab_print_search((char *) argv->buffer);
printf("Duplicate symbol, be more specific.\n");
} else if (rc == EOK) {
if (pointer)
addr = *(uintptr_t *) addr;
printf("Writing %#" PRIx64 " -> %p\n", arg1, addr);
*(uint32_t *) addr = arg1;
} else {
printf("No symbol information available.\n");
}
return 1;
}
 
/** Command for listings SLAB caches
*
* @param argv Ignores
*
* @return Always 1
*/
int cmd_slabs(cmd_arg_t * argv) {
slab_print_list();
return 1;
}
 
 
/** Command for listings Thread information
*
* @param argv Ignores
*
* @return Always 1
*/
int cmd_threads(cmd_arg_t * argv) {
thread_print_list();
return 1;
}
 
/** Command for listings Task information
*
* @param argv Ignores
*
* @return Always 1
*/
int cmd_tasks(cmd_arg_t * argv) {
task_print_list();
return 1;
}
 
/** Command for listings Thread information
*
* @param argv Ignores
*
* @return Always 1
*/
int cmd_sched(cmd_arg_t * argv) {
sched_print_list();
return 1;
}
 
/** Command for listing memory zones
*
* @param argv Ignored
*
* return Always 1
*/
int cmd_zones(cmd_arg_t * argv) {
zone_print_list();
return 1;
}
 
/** Command for memory zone details
*
* @param argv Integer argument from cmdline expected
*
* return Always 1
*/
int cmd_zone(cmd_arg_t * argv) {
zone_print_one(argv[0].intval);
return 1;
}
 
/** Command for printing task ipc details
*
* @param argv Integer argument from cmdline expected
*
* return Always 1
*/
int cmd_ipc(cmd_arg_t * argv) {
ipc_print_task(argv[0].intval);
return 1;
}
 
 
/** Command for listing processors.
*
* @param argv Ignored.
*
* return Always 1.
*/
int cmd_cpus(cmd_arg_t *argv)
{
cpu_list();
return 1;
}
 
/** Command for printing kernel version.
*
* @param argv Ignored.
*
* return Always 1.
*/
int cmd_version(cmd_arg_t *argv)
{
version_print();
return 1;
}
 
/** Command for returning console back to userspace.
*
* @param argv Ignored.
*
* return Always 1.
*/
int cmd_continue(cmd_arg_t *argv)
{
printf("The kernel will now relinquish the console.\n");
release_console();
event_notify_0(EVENT_KCONSOLE);
indev_pop_character(stdin);
return 1;
}
 
#ifdef CONFIG_TEST
/** Command for printing kernel tests list.
*
* @param argv Ignored.
*
* return Always 1.
*/
int cmd_tests(cmd_arg_t *argv)
{
size_t len = 0;
test_t *test;
for (test = tests; test->name != NULL; test++) {
if (str_length(test->name) > len)
len = str_length(test->name);
}
for (test = tests; test->name != NULL; test++)
printf("%-*s %s%s\n", len, test->name, test->desc, (test->safe ? "" : " (unsafe)"));
printf("%-*s Run all safe tests\n", len, "*");
return 1;
}
 
static bool run_test(const test_t *test)
{
printf("%s (%s)\n", test->name, test->desc);
/* Update and read thread accounting
for benchmarking */
ipl_t ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
uint64_t t0 = task_get_accounting(TASK);
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
/* Execute the test */
test_quiet = false;
char *ret = test->entry();
/* Update and read thread accounting */
ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
uint64_t dt = task_get_accounting(TASK) - t0;
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
uint64_t cycles;
char suffix;
order(dt, &cycles, &suffix);
printf("Time: %" PRIu64 "%c cycles\n", cycles, suffix);
if (ret == NULL) {
printf("Test passed\n");
return true;
}
 
printf("%s\n", ret);
return false;
}
 
static bool run_bench(const test_t *test, const uint32_t cnt)
{
uint32_t i;
bool ret = true;
uint64_t cycles;
char suffix;
if (cnt < 1)
return true;
uint64_t *data = (uint64_t *) malloc(sizeof(uint64_t) * cnt, 0);
if (data == NULL) {
printf("Error allocating memory for statistics\n");
return false;
}
for (i = 0; i < cnt; i++) {
printf("%s (%u/%u) ... ", test->name, i + 1, cnt);
/* Update and read thread accounting
for benchmarking */
ipl_t ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
uint64_t t0 = task_get_accounting(TASK);
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
/* Execute the test */
test_quiet = true;
char * ret = test->entry();
/* Update and read thread accounting */
ipl = interrupts_disable();
spinlock_lock(&TASK->lock);
uint64_t dt = task_get_accounting(TASK) - t0;
spinlock_unlock(&TASK->lock);
interrupts_restore(ipl);
if (ret != NULL) {
printf("%s\n", ret);
ret = false;
break;
}
data[i] = dt;
order(dt, &cycles, &suffix);
printf("OK (%" PRIu64 "%c cycles)\n", cycles, suffix);
}
if (ret) {
printf("\n");
uint64_t sum = 0;
for (i = 0; i < cnt; i++) {
sum += data[i];
}
order(sum / (uint64_t) cnt, &cycles, &suffix);
printf("Average\t\t%" PRIu64 "%c\n", cycles, suffix);
}
free(data);
return ret;
}
 
/** Command for returning kernel tests
*
* @param argv Argument vector.
*
* return Always 1.
*/
int cmd_test(cmd_arg_t *argv)
{
test_t *test;
if (str_cmp((char *) argv->buffer, "*") == 0) {
for (test = tests; test->name != NULL; test++) {
if (test->safe) {
printf("\n");
if (!run_test(test))
break;
}
}
} else {
bool fnd = false;
for (test = tests; test->name != NULL; test++) {
if (str_cmp(test->name, (char *) argv->buffer) == 0) {
fnd = true;
run_test(test);
break;
}
}
if (!fnd)
printf("Unknown test\n");
}
return 1;
}
 
/** Command for returning kernel tests as benchmarks
*
* @param argv Argument vector.
*
* return Always 1.
*/
int cmd_bench(cmd_arg_t *argv)
{
test_t *test;
uint32_t cnt = argv[1].intval;
if (str_cmp((char *) argv->buffer, "*") == 0) {
for (test = tests; test->name != NULL; test++) {
if (test->safe) {
if (!run_bench(test, cnt))
break;
}
}
} else {
bool fnd = false;
for (test = tests; test->name != NULL; test++) {
if (str_cmp(test->name, (char *) argv->buffer) == 0) {
fnd = true;
if (test->safe)
run_bench(test, cnt);
else
printf("Unsafe test\n");
break;
}
}
if (!fnd)
printf("Unknown test\n");
}
return 1;
}
 
#endif
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/console/kconsole.c
0,0 → 1,689
/*
* Copyright (c) 2005 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup genericconsole
* @{
*/
 
/**
* @file kconsole.c
* @brief Kernel console.
*
* This file contains kernel thread managing the kernel console.
*
*/
 
#include <console/kconsole.h>
#include <console/console.h>
#include <console/chardev.h>
#include <console/cmd.h>
#include <print.h>
#include <panic.h>
#include <arch/types.h>
#include <adt/list.h>
#include <arch.h>
#include <macros.h>
#include <debug.h>
#include <func.h>
#include <string.h>
#include <macros.h>
#include <sysinfo/sysinfo.h>
#include <ddi/device.h>
#include <symtab.h>
#include <errno.h>
#include <putchar.h>
#include <string.h>
 
/** Simple kernel console.
*
* The console is realized by kernel thread kconsole.
* It doesn't understand any useful command on its own,
* but makes it possible for other kernel subsystems to
* register their own commands.
*/
 
/** Locking.
*
* There is a list of cmd_info_t structures. This list
* is protected by cmd_lock spinlock. Note that specially
* the link elements of cmd_info_t are protected by
* this lock.
*
* Each cmd_info_t also has its own lock, which protects
* all elements thereof except the link element.
*
* cmd_lock must be acquired before any cmd_info lock.
* When locking two cmd info structures, structure with
* lower address must be locked first.
*/
 
SPINLOCK_INITIALIZE(cmd_lock); /**< Lock protecting command list. */
LIST_INITIALIZE(cmd_head); /**< Command list. */
 
static wchar_t history[KCONSOLE_HISTORY][MAX_CMDLINE] = {};
static size_t history_pos = 0;
 
/** Initialize kconsole data structures
*
* This is the most basic initialization, almost no
* other kernel subsystem is ready yet.
*
*/
void kconsole_init(void)
{
unsigned int i;
cmd_init();
for (i = 0; i < KCONSOLE_HISTORY; i++)
history[i][0] = 0;
}
 
/** Register kconsole command.
*
* @param cmd Structure describing the command.
*
* @return False on failure, true on success.
*
*/
bool cmd_register(cmd_info_t *cmd)
{
link_t *cur;
spinlock_lock(&cmd_lock);
/*
* Make sure the command is not already listed.
*/
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp = list_get_instance(cur, cmd_info_t, link);
if (hlp == cmd) {
/* The command is already there. */
spinlock_unlock(&cmd_lock);
return false;
}
/* Avoid deadlock. */
if (hlp < cmd) {
spinlock_lock(&hlp->lock);
spinlock_lock(&cmd->lock);
} else {
spinlock_lock(&cmd->lock);
spinlock_lock(&hlp->lock);
}
if (str_cmp(hlp->name, cmd->name) == 0) {
/* The command is already there. */
spinlock_unlock(&hlp->lock);
spinlock_unlock(&cmd->lock);
spinlock_unlock(&cmd_lock);
return false;
}
spinlock_unlock(&hlp->lock);
spinlock_unlock(&cmd->lock);
}
/*
* Now the command can be added.
*/
list_append(&cmd->link, &cmd_head);
spinlock_unlock(&cmd_lock);
return true;
}
 
/** Print count times a character */
static void print_cc(wchar_t ch, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
putchar(ch);
}
 
/** Try to find a command beginning with prefix */
static const char *cmdtab_search_one(const char *name, link_t **startpos)
{
size_t namelen = str_length(name);
spinlock_lock(&cmd_lock);
if (*startpos == NULL)
*startpos = cmd_head.next;
for (; *startpos != &cmd_head; *startpos = (*startpos)->next) {
cmd_info_t *hlp = list_get_instance(*startpos, cmd_info_t, link);
const char *curname = hlp->name;
if (str_length(curname) < namelen)
continue;
if (str_lcmp(curname, name, namelen) == 0) {
spinlock_unlock(&cmd_lock);
return (curname + str_lsize(curname, namelen));
}
}
spinlock_unlock(&cmd_lock);
return NULL;
}
 
/** Command completion of the commands
*
* @param name String to match, changed to hint on exit
* @param size Input buffer size
*
* @return Number of found matches
*
*/
static int cmdtab_compl(char *input, size_t size)
{
const char *name = input;
size_t found = 0;
link_t *pos = NULL;
const char *hint;
char output[MAX_CMDLINE];
output[0] = 0;
while ((hint = cmdtab_search_one(name, &pos))) {
if ((found == 0) || (str_length(output) > str_length(hint)))
str_cpy(output, MAX_CMDLINE, hint);
pos = pos->next;
found++;
}
if ((found > 1) && (str_length(output) != 0)) {
printf("\n");
pos = NULL;
while ((hint = cmdtab_search_one(name, &pos))) {
cmd_info_t *hlp = list_get_instance(pos, cmd_info_t, link);
printf("%s (%s)\n", hlp->name, hlp->description);
pos = pos->next;
}
}
if (found > 0)
str_cpy(input, size, output);
return found;
}
 
static wchar_t *clever_readline(const char *prompt, indev_t *indev)
{
printf("%s> ", prompt);
size_t position = 0;
wchar_t *current = history[history_pos];
current[0] = 0;
while (true) {
wchar_t ch = indev_pop_character(indev);
if (ch == '\n') {
/* Enter */
putchar(ch);
break;
}
if (ch == '\b') {
/* Backspace */
if (position == 0)
continue;
if (wstr_remove(current, position - 1)) {
position--;
putchar('\b');
printf("%ls ", current + position);
print_cc('\b', wstr_length(current) - position + 1);
continue;
}
}
if (ch == '\t') {
/* Tab completion */
/* Move to the end of the word */
for (; (current[position] != 0) && (!isspace(current[position]));
position++)
putchar(current[position]);
if (position == 0)
continue;
/* Find the beginning of the word
and copy it to tmp */
size_t beg;
for (beg = position - 1; (beg > 0) && (!isspace(current[beg]));
beg--);
if (isspace(current[beg]))
beg++;
char tmp[STR_BOUNDS(MAX_CMDLINE)];
wstr_nstr(tmp, current + beg, position - beg + 1);
int found;
if (beg == 0) {
/* Command completion */
found = cmdtab_compl(tmp, STR_BOUNDS(MAX_CMDLINE));
} else {
/* Symbol completion */
found = symtab_compl(tmp, STR_BOUNDS(MAX_CMDLINE));
}
if (found == 0)
continue;
if (found > 1) {
/* No unique hint, list was printed */
printf("%s> ", prompt);
printf("%ls", current);
print_cc('\b', wstr_length(current) - position);
continue;
}
/* We have a hint */
size_t off = 0;
size_t i = 0;
while ((ch = str_decode(tmp, &off, STR_NO_LIMIT)) != 0) {
if (!wstr_linsert(current, ch, position + i, MAX_CMDLINE))
break;
i++;
}
printf("%ls", current + position);
position += str_length(tmp);
print_cc('\b', wstr_length(current) - position);
if (position == wstr_length(current)) {
/* Insert a space after the last completed argument */
if (wstr_linsert(current, ' ', position, MAX_CMDLINE)) {
printf("%ls", current + position);
position++;
}
}
continue;
}
if (ch == U_LEFT_ARROW) {
/* Left */
if (position > 0) {
putchar('\b');
position--;
}
continue;
}
if (ch == U_RIGHT_ARROW) {
/* Right */
if (position < wstr_length(current)) {
putchar(current[position]);
position++;
}
continue;
}
if ((ch == U_UP_ARROW) || (ch == U_DOWN_ARROW)) {
/* Up, down */
print_cc('\b', position);
print_cc(' ', wstr_length(current));
print_cc('\b', wstr_length(current));
if (ch == U_UP_ARROW) {
/* Up */
if (history_pos == 0)
history_pos = KCONSOLE_HISTORY - 1;
else
history_pos--;
} else {
/* Down */
history_pos++;
history_pos = history_pos % KCONSOLE_HISTORY;
}
current = history[history_pos];
printf("%ls", current);
position = wstr_length(current);
continue;
}
if (ch == U_HOME_ARROW) {
/* Home */
print_cc('\b', position);
position = 0;
continue;
}
if (ch == U_END_ARROW) {
/* End */
printf("%ls", current + position);
position = wstr_length(current);
continue;
}
if (ch == U_DELETE) {
/* Delete */
if (position == wstr_length(current))
continue;
if (wstr_remove(current, position)) {
printf("%ls ", current + position);
print_cc('\b', wstr_length(current) - position + 1);
}
continue;
}
if (wstr_linsert(current, ch, position, MAX_CMDLINE)) {
printf("%ls", current + position);
position++;
print_cc('\b', wstr_length(current) - position);
}
}
if (wstr_length(current) > 0) {
history_pos++;
history_pos = history_pos % KCONSOLE_HISTORY;
}
return current;
}
 
bool kconsole_check_poll(void)
{
return check_poll(stdin);
}
 
static bool parse_int_arg(const char *text, size_t len, unative_t *result)
{
bool isaddr = false;
bool isptr = false;
/* If we get a name, try to find it in symbol table */
if (text[0] == '&') {
isaddr = true;
text++;
len--;
} else if (text[0] == '*') {
isptr = true;
text++;
len--;
}
if ((text[0] < '0') || (text[0] > '9')) {
char symname[MAX_SYMBOL_NAME];
str_ncpy(symname, MAX_SYMBOL_NAME, text, len + 1);
uintptr_t symaddr;
int rc = symtab_addr_lookup(symname, &symaddr);
switch (rc) {
case ENOENT:
printf("Symbol %s not found.\n", symname);
return false;
case EOVERFLOW:
printf("Duplicate symbol %s.\n", symname);
symtab_print_search(symname);
return false;
case ENOTSUP:
printf("No symbol information available.\n");
return false;
}
if (isaddr)
*result = (unative_t) symaddr;
else if (isptr)
*result = **((unative_t **) symaddr);
else
*result = *((unative_t *) symaddr);
} else {
/* It's a number - convert it */
*result = atoi(text);
if (isptr)
*result = *((unative_t *) *result);
}
return true;
}
 
/** Parse argument.
*
* Find start and end positions of command line argument.
*
* @param cmdline Command line as read from the input device.
* @param size Size (in bytes) of the string.
* @param start On entry, 'start' contains pointer to the offset
* of the first unprocessed character of cmdline.
* On successful exit, it marks beginning of the next argument.
* @param end Undefined on entry. On exit, 'end' is the offset of the first
* character behind the next argument.
*
* @return False on failure, true on success.
*
*/
static bool parse_argument(const char *cmdline, size_t size, size_t *start, size_t *end)
{
ASSERT(start != NULL);
ASSERT(end != NULL);
bool found_start = false;
size_t offset = *start;
size_t prev = *start;
wchar_t ch;
while ((ch = str_decode(cmdline, &offset, size)) != 0) {
if (!found_start) {
if (!isspace(ch)) {
*start = prev;
found_start = true;
}
} else {
if (isspace(ch))
break;
}
prev = offset;
}
*end = prev;
return found_start;
}
 
/** Parse command line.
*
* @param cmdline Command line as read from input device.
* @param size Size (in bytes) of the string.
*
* @return Structure describing the command.
*
*/
static cmd_info_t *parse_cmdline(const char *cmdline, size_t size)
{
size_t start = 0;
size_t end = 0;
if (!parse_argument(cmdline, size, &start, &end)) {
/* Command line did not contain alphanumeric word. */
return NULL;
}
spinlock_lock(&cmd_lock);
cmd_info_t *cmd = NULL;
link_t *cur;
for (cur = cmd_head.next; cur != &cmd_head; cur = cur->next) {
cmd_info_t *hlp = list_get_instance(cur, cmd_info_t, link);
spinlock_lock(&hlp->lock);
if (str_lcmp(hlp->name, cmdline + start,
max(str_length(hlp->name),
str_nlength(cmdline + start, (size_t) (end - start) - 1))) == 0) {
cmd = hlp;
break;
}
spinlock_unlock(&hlp->lock);
}
spinlock_unlock(&cmd_lock);
if (!cmd) {
/* Unknown command. */
printf("Unknown command.\n");
return NULL;
}
/* cmd == hlp is locked */
/*
* The command line must be further analyzed and
* the parameters therefrom must be matched and
* converted to those specified in the cmd info
* structure.
*/
bool error = false;
size_t i;
for (i = 0; i < cmd->argc; i++) {
start = end;
if (!parse_argument(cmdline, size, &start, &end)) {
printf("Too few arguments.\n");
spinlock_unlock(&cmd->lock);
return NULL;
}
char *buf;
switch (cmd->argv[i].type) {
case ARG_TYPE_STRING:
buf = (char *) cmd->argv[i].buffer;
str_ncpy(buf, cmd->argv[i].len, cmdline + start,
end - start);
break;
case ARG_TYPE_INT:
if (!parse_int_arg(cmdline + start, end - start,
&cmd->argv[i].intval))
error = true;
break;
case ARG_TYPE_VAR:
if ((start < end - 1) && (cmdline[start] == '"')) {
if (cmdline[end - 1] == '"') {
buf = (char *) cmd->argv[i].buffer;
str_ncpy(buf, cmd->argv[i].len,
cmdline + start + 1,
(end - start) - 1);
cmd->argv[i].intval = (unative_t) buf;
cmd->argv[i].vartype = ARG_TYPE_STRING;
} else {
printf("Wrong synxtax.\n");
error = true;
}
} else if (parse_int_arg(cmdline + start,
end - start, &cmd->argv[i].intval)) {
cmd->argv[i].vartype = ARG_TYPE_INT;
} else {
printf("Unrecognized variable argument.\n");
error = true;
}
break;
case ARG_TYPE_INVALID:
default:
printf("Invalid argument type\n");
error = true;
break;
}
}
if (error) {
spinlock_unlock(&cmd->lock);
return NULL;
}
start = end;
if (parse_argument(cmdline, size, &start, &end)) {
printf("Too many arguments.\n");
spinlock_unlock(&cmd->lock);
return NULL;
}
spinlock_unlock(&cmd->lock);
return cmd;
}
 
/** Kernel console prompt.
*
* @param prompt Kernel console prompt (e.g kconsole/panic).
* @param msg Message to display in the beginning.
* @param kcon Wait for keypress to show the prompt
* and never exit.
*
*/
void kconsole(char *prompt, char *msg, bool kcon)
{
if (!stdin) {
LOG("No stdin for kernel console");
return;
}
if (msg)
printf("%s", msg);
if (kcon)
indev_pop_character(stdin);
else
printf("Type \"exit\" to leave the console.\n");
while (true) {
wchar_t *tmp = clever_readline((char *) prompt, stdin);
size_t len = wstr_length(tmp);
if (!len)
continue;
char cmdline[STR_BOUNDS(MAX_CMDLINE)];
wstr_nstr(cmdline, tmp, STR_BOUNDS(MAX_CMDLINE));
if ((!kcon) && (len == 4) && (str_lcmp(cmdline, "exit", 4) == 0))
break;
cmd_info_t *cmd_info = parse_cmdline(cmdline, STR_BOUNDS(MAX_CMDLINE));
if (!cmd_info)
continue;
(void) cmd_info->func(cmd_info->argv);
}
}
 
/** Kernel console managing thread.
*
*/
void kconsole_thread(void *data)
{
kconsole("kconsole", "Kernel console ready (press any key to activate)\n", true);
}
 
/** @}
*/
/tags/0.4.1/kernel/generic/src/console/chardev.c
0,0 → 1,151
/*
* Copyright (c) 2005 Jakub Jermar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
/** @addtogroup genericconsole
* @{
*/
/** @file
*/
 
#include <console/chardev.h>
#include <synch/waitq.h>
#include <synch/spinlock.h>
#include <print.h>
#include <func.h>
#include <arch.h>
 
/** Initialize input character device.
*
* @param indev Input character device.
* @param op Implementation of input character device operations.
*
*/
void indev_initialize(char *name, indev_t *indev,
indev_operations_t *op)
{
indev->name = name;
waitq_initialize(&indev->wq);
spinlock_initialize(&indev->lock, "indev");
indev->counter = 0;
indev->index = 0;
indev->op = op;
}
 
/** Push character read from input character device.
*
* @param indev Input character device.
* @param ch Character being pushed.
*
*/
void indev_push_character(indev_t *indev, wchar_t ch)
{
ASSERT(indev);
spinlock_lock(&indev->lock);
if (indev->counter == INDEV_BUFLEN - 1) {
/* Buffer full */
spinlock_unlock(&indev->lock);
return;
}
indev->counter++;
indev->buffer[indev->index++] = ch;
/* Index modulo size of buffer */
indev->index = indev->index % INDEV_BUFLEN;
waitq_wakeup(&indev->wq, WAKEUP_FIRST);
spinlock_unlock(&indev->lock);
}
 
/** Pop character from input character device.
*
* @param indev Input character device.
*
* @return Character read.
*
*/
wchar_t indev_pop_character(indev_t *indev)
{
if (atomic_get(&haltstate)) {
/* If we are here, we are hopefully on the processor that
* issued the 'halt' command, so proceed to read the character
* directly from input
*/
if (check_poll(indev))
return indev->op->poll(indev);
/* No other way of interacting with user */
interrupts_disable();
if (CPU)
printf("cpu%u: ", CPU->id);
else
printf("cpu: ");
printf("halted (no polling input)\n");
cpu_halt();
}
waitq_sleep(&indev->wq);
ipl_t ipl = interrupts_disable();
spinlock_lock(&indev->lock);
wchar_t ch = indev->buffer[(indev->index - indev->counter) % INDEV_BUFLEN];
indev->counter--;
spinlock_unlock(&indev->lock);
interrupts_restore(ipl);
return ch;
}
 
/** Initialize output character device.
*
* @param outdev Output character device.
* @param op Implementation of output character device operations.
*
*/
void outdev_initialize(char *name, outdev_t *outdev,
outdev_operations_t *op)
{
outdev->name = name;
spinlock_initialize(&outdev->lock, "outdev");
outdev->op = op;
}
 
bool check_poll(indev_t *indev)
{
if (indev == NULL)
return false;
if (indev->op == NULL)
return false;
return (indev->op->poll != NULL);
}
 
/** @}
*/