HelenOS sources

root/kernel/generic/src/console/console.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. stdin_wire
  2. stdin_signal
  3. stdout_wire
  4. stdout_write
  5. stdout_redraw
  6. stdout_scroll_up
  7. stdout_scroll_down
  8. kio_init
  9. grab_console
  10. release_console
  11. sys_debug_console
  12. kio_update
  13. kio_flush
  14. kio_push_char
  15. putuchar
  16. sys_kio

/*
 * 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 kernel_generic_console
 * @{
 */
/** @file
 */

#include <assert.h>
#include <console/console.h>
#include <console/chardev.h>
#include <sysinfo/sysinfo.h>
#include <synch/waitq.h>
#include <synch/spinlock.h>
#include <typedefs.h>
#include <ddi/irq.h>
#include <ddi/ddi.h>
#include <ipc/event.h>
#include <ipc/irq.h>
#include <arch.h>
#include <panic.h>
#include <stdio.h>
#include <putchar.h>
#include <atomic.h>
#include <syscall/copy.h>
#include <errno.h>
#include <str.h>
#include <stdatomic.h>
#include <abi/kio.h>
#include <mm/frame.h> /* SIZE2FRAMES */
#include <stdlib.h>  /* malloc */

#define KIO_PAGES    8
#define KIO_LENGTH   (KIO_PAGES * PAGE_SIZE / sizeof(char32_t))

/** Kernel log cyclic buffer */
char32_t kio[KIO_LENGTH] __attribute__((aligned(PAGE_SIZE)));

/** Kernel log initialized */
static atomic_bool kio_inited = ATOMIC_VAR_INIT(false);

/** First kernel log characters */
static size_t kio_start = 0;

/** Number of valid kernel log characters */
static size_t kio_len = 0;

/** Number of stored (not printed) kernel log characters */
static size_t kio_stored = 0;

/** Number of stored kernel log characters for uspace */
static size_t kio_uspace = 0;

/** Kernel log spinlock */
SPINLOCK_INITIALIZE_NAME(kio_lock, "kio_lock");

/** Physical memory area used for kio buffer */
static parea_t kio_parea;

static indev_t stdin_sink;
static outdev_t stdout_source;

static void stdin_signal(indev_t *, indev_signal_t);

static indev_operations_t stdin_ops = {
        .poll = NULL,
        .signal = stdin_signal
};

static void stdout_write(outdev_t *, char32_t);
static void stdout_redraw(outdev_t *);
static void stdout_scroll_up(outdev_t *);
static void stdout_scroll_down(outdev_t *);

static outdev_operations_t stdout_ops = {
        .write = stdout_write,
        .redraw = stdout_redraw,
        .scroll_up = stdout_scroll_up,
        .scroll_down = stdout_scroll_down
};

/** Override kernel console lockout */
bool console_override = false;

/** Standard input and output character devices */
indev_t *stdin = NULL;
outdev_t *stdout = NULL;

indev_t *stdin_wire(void)
{
        if (stdin == NULL) {
                indev_initialize("stdin", &stdin_sink, &stdin_ops);
                stdin = &stdin_sink;
        }

        return stdin;
}

static void stdin_signal(indev_t *indev, indev_signal_t signal)
{
        switch (signal) {
        case INDEV_SIGNAL_SCROLL_UP:
                if (stdout != NULL)
                        stdout_scroll_up(stdout);
                break;
        case INDEV_SIGNAL_SCROLL_DOWN:
                if (stdout != NULL)
                        stdout_scroll_down(stdout);
                break;
        }
}

void stdout_wire(outdev_t *outdev)
{
        if (stdout == NULL) {
                outdev_initialize("stdout", &stdout_source, &stdout_ops);
                stdout = &stdout_source;
        }

        list_append(&outdev->link, &stdout->list);
}

static void stdout_write(outdev_t *dev, char32_t ch)
{
        list_foreach(dev->list, link, outdev_t, sink) {
                if ((sink) && (sink->op->write))
                        sink->op->write(sink, ch);
        }
}

static void stdout_redraw(outdev_t *dev)
{
        list_foreach(dev->list, link, outdev_t, sink) {
                if ((sink) && (sink->op->redraw))
                        sink->op->redraw(sink);
        }
}

static void stdout_scroll_up(outdev_t *dev)
{
        list_foreach(dev->list, link, outdev_t, sink) {
                if ((sink) && (sink->op->scroll_up))
                        sink->op->scroll_up(sink);
        }
}

static void stdout_scroll_down(outdev_t *dev)
{
        list_foreach(dev->list, link, outdev_t, sink) {
                if ((sink) && (sink->op->scroll_down))
                        sink->op->scroll_down(sink);
        }
}

/** 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 kio_init(void)
{
        void *faddr = (void *) KA2PA(kio);

        assert((uintptr_t) faddr % FRAME_SIZE == 0);

        ddi_parea_init(&kio_parea);
        kio_parea.pbase = (uintptr_t) faddr;
        kio_parea.frames = SIZE2FRAMES(sizeof(kio));
        kio_parea.unpriv = false;
        kio_parea.mapped = false;
        ddi_parea_register(&kio_parea);

        sysinfo_set_item_val("kio.faddr", NULL, (sysarg_t) faddr);
        sysinfo_set_item_val("kio.pages", NULL, KIO_PAGES);

        event_set_unmask_callback(EVENT_KIO, kio_update);
        atomic_store(&kio_inited, true);
}

void grab_console(void)
{
        sysinfo_set_item_val("kconsole", NULL, true);
        event_notify_1(EVENT_KCONSOLE, false, true);
        bool prev = console_override;

        console_override = true;
        if ((stdout) && (stdout->op->redraw))
                stdout->op->redraw(stdout);

        if ((stdin) && (!prev)) {
                /*
                 * Force the console to print the prompt.
                 */
                indev_push_character(stdin, '\n');
        }
}

void release_console(void)
{
        sysinfo_set_item_val("kconsole", NULL, false);
        console_override = false;
        event_notify_1(EVENT_KCONSOLE, false, false);
}

/** Activate kernel console override */
sysarg_t sys_debug_console(void)
{
#ifdef CONFIG_KCONSOLE
        grab_console();
        return true;
#else
        return false;
#endif
}

void kio_update(void *event)
{
        if (!atomic_load(&kio_inited))
                return;

        spinlock_lock(&kio_lock);

        if (kio_uspace > 0) {
                if (event_notify_3(EVENT_KIO, true, kio_start, kio_len,
                    kio_uspace) == EOK)
                        kio_uspace = 0;
        }

        spinlock_unlock(&kio_lock);
}

/** Flush characters that are stored in the output buffer
 *
 */
void kio_flush(void)
{
        bool ordy = ((stdout) && (stdout->op->write));

        if (!ordy)
                return;

        spinlock_lock(&kio_lock);

        /* Print characters that weren't printed earlier */
        while (kio_stored > 0) {
                char32_t tmp = kio[(kio_start + kio_len - kio_stored) % KIO_LENGTH];
                kio_stored--;

                /*
                 * We need to give up the spinlock for
                 * the physical operation of writing out
                 * the character.
                 */
                spinlock_unlock(&kio_lock);
                stdout->op->write(stdout, tmp);
                spinlock_lock(&kio_lock);
        }

        spinlock_unlock(&kio_lock);
}

/** Put a character into the output buffer.
 *
 * The caller is required to hold kio_lock
 */
void kio_push_char(const char32_t ch)
{
        kio[(kio_start + kio_len) % KIO_LENGTH] = ch;
        if (kio_len < KIO_LENGTH)
                kio_len++;
        else
                kio_start = (kio_start + 1) % KIO_LENGTH;

        if (kio_stored < kio_len)
                kio_stored++;

        /* The character is stored for uspace */
        if (kio_uspace < kio_len)
                kio_uspace++;
}

void putuchar(const char32_t ch)
{
        bool ordy = ((stdout) && (stdout->op->write));

        spinlock_lock(&kio_lock);
        kio_push_char(ch);
        spinlock_unlock(&kio_lock);

        /* Output stored characters */
        kio_flush();

        if (!ordy) {
                /*
                 * No standard output routine defined yet.
                 * The character is still stored in the kernel log
                 * for possible future output.
                 *
                 * The early_putuchar() function is used to output
                 * the character for low-level debugging purposes.
                 * Note that the early_putuchar() function might be
                 * a no-op on certain hardware configurations.
                 */
                early_putuchar(ch);
        }

        /* Force notification on newline */
        if (ch == '\n')
                kio_update(NULL);
}

/** Print using kernel facility
 *
 * Print to kernel log.
 *
 */
sys_errno_t sys_kio(int cmd, uspace_addr_t buf, size_t size)
{
        char *data;
        errno_t rc;

        switch (cmd) {
        case KIO_UPDATE:
                kio_update(NULL);
                return EOK;
        case KIO_WRITE:
        case KIO_COMMAND:
                break;
        default:
                return ENOTSUP;
        }

        if (size > PAGE_SIZE)
                return (sys_errno_t) ELIMIT;

        if (size > 0) {
                data = (char *) malloc(size + 1);
                if (!data)
                        return (sys_errno_t) ENOMEM;

                rc = copy_from_uspace(data, buf, size);
                if (rc) {
                        free(data);
                        return (sys_errno_t) rc;
                }
                data[size] = 0;

                switch (cmd) {
                case KIO_WRITE:
                        printf("[%s(%lu)] %s\n", TASK->name,
                            (unsigned long) TASK->taskid, data);
                        break;
                case KIO_COMMAND:
                        if (!stdin)
                                break;
                        for (unsigned int i = 0; i < size; i++)
                                indev_push_character(stdin, data[i]);
                        indev_push_character(stdin, '\n');
                        break;
                }

                free(data);
        }

        return EOK;
}

/** @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub