Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/console/console.c

    r28a5ebd r39e1b9a  
    22 * Copyright (c) 2003 Josef Cejka
    33 * Copyright (c) 2005 Jakub Jermar
     4 * Copyright (c) 2025 Jiří Zárevúcky
    45 * All rights reserved.
    56 *
     
    3435 */
    3536
    36 #include <assert.h>
     37#include <abi/kio.h>
     38#include <console/chardev.h>
    3739#include <console/console.h>
    38 #include <console/chardev.h>
     40#include <errno.h>
     41#include <ipc/event.h>
     42#include <log.h>
     43#include <panic.h>
     44#include <preemption.h>
     45#include <proc/task.h>
     46#include <stdatomic.h>
     47#include <stdio.h>
     48#include <stdlib.h>  /* malloc */
     49#include <str.h>
     50#include <synch/mutex.h>
     51#include <synch/spinlock.h>
     52#include <syscall/copy.h>
    3953#include <sysinfo/sysinfo.h>
    40 #include <synch/waitq.h>
    41 #include <synch/spinlock.h>
    42 #include <typedefs.h>
    43 #include <ddi/irq.h>
    44 #include <ddi/ddi.h>
    45 #include <ipc/event.h>
    46 #include <ipc/irq.h>
    47 #include <arch.h>
    48 #include <panic.h>
    49 #include <stdio.h>
    50 #include <putchar.h>
    51 #include <atomic.h>
    52 #include <syscall/copy.h>
    53 #include <errno.h>
    54 #include <str.h>
    55 #include <stdatomic.h>
    56 #include <abi/kio.h>
    57 #include <mm/frame.h> /* SIZE2FRAMES */
    58 #include <stdlib.h>  /* malloc */
    5954
    6055#define KIO_PAGES    8
    61 #define KIO_LENGTH   (KIO_PAGES * PAGE_SIZE / sizeof(char32_t))
     56#define KIO_LENGTH   (KIO_PAGES * PAGE_SIZE)
    6257
    6358/** Kernel log cyclic buffer */
    64 char32_t kio[KIO_LENGTH] __attribute__((aligned(PAGE_SIZE)));
     59static char kio[KIO_LENGTH];
    6560
    6661/** Kernel log initialized */
    6762static atomic_bool kio_inited = ATOMIC_VAR_INIT(false);
    6863
    69 /** First kernel log characters */
    70 static size_t kio_start = 0;
    71 
    72 /** Number of valid kernel log characters */
    73 static size_t kio_len = 0;
    74 
    75 /** Number of stored (not printed) kernel log characters */
    76 static size_t kio_stored = 0;
    77 
    78 /** Number of stored kernel log characters for uspace */
    79 static size_t kio_uspace = 0;
     64/** A mutex for preventing interleaving of output lines from different threads.
     65 * May not be held in some circumstances, so locking of any internal shared
     66 * structures is still necessary.
     67 */
     68static MUTEX_INITIALIZE(console_mutex, MUTEX_RECURSIVE);
     69
     70/** Number of characters written to buffer. Periodically overflows. */
     71static size_t kio_written = 0;
     72
     73/** Number of characters written to output devices. Periodically overflows. */
     74static size_t kio_processed = 0;
     75
     76/** Last notification sent to uspace. */
     77static size_t kio_notified = 0;
    8078
    8179/** Kernel log spinlock */
    82 SPINLOCK_INITIALIZE_NAME(kio_lock, "kio_lock");
    83 
    84 /** Physical memory area used for kio buffer */
    85 static parea_t kio_parea;
     80IRQ_SPINLOCK_INITIALIZE(kio_lock);
     81
     82static IRQ_SPINLOCK_INITIALIZE(flush_lock);
     83
     84static IRQ_SPINLOCK_INITIALIZE(early_mbstate_lock);
     85static mbstate_t early_mbstate;
    8686
    8787static indev_t stdin_sink;
     
    9595};
    9696
    97 static void stdout_write(outdev_t *, char32_t);
     97static void stdout_write(outdev_t *, const char *, size_t);
    9898static void stdout_redraw(outdev_t *);
    9999static void stdout_scroll_up(outdev_t *);
     
    148148}
    149149
    150 static void stdout_write(outdev_t *dev, char32_t ch)
     150static void stdout_write(outdev_t *dev, const char *s, size_t n)
    151151{
    152152        list_foreach(dev->list, link, outdev_t, sink) {
    153153                if ((sink) && (sink->op->write))
    154                         sink->op->write(sink, ch);
     154                        sink->op->write(sink, s, n);
    155155        }
    156156}
     
    181181
    182182/** Initialize kernel logging facility
    183  *
    184  * The shared area contains kernel cyclic buffer. Userspace application may
    185  * be notified on new data with indication of position and size
    186  * of the data within the circular buffer.
    187  *
    188183 */
    189184void kio_init(void)
    190185{
    191         void *faddr = (void *) KA2PA(kio);
    192 
    193         assert((uintptr_t) faddr % FRAME_SIZE == 0);
    194 
    195         ddi_parea_init(&kio_parea);
    196         kio_parea.pbase = (uintptr_t) faddr;
    197         kio_parea.frames = SIZE2FRAMES(sizeof(kio));
    198         kio_parea.unpriv = false;
    199         kio_parea.mapped = false;
    200         ddi_parea_register(&kio_parea);
    201 
    202         sysinfo_set_item_val("kio.faddr", NULL, (sysarg_t) faddr);
    203         sysinfo_set_item_val("kio.pages", NULL, KIO_PAGES);
    204 
    205186        event_set_unmask_callback(EVENT_KIO, kio_update);
    206187        atomic_store(&kio_inited, true);
     
    243224}
    244225
    245 /** Get string from input character device.
    246  *
    247  * Read characters from input character device until first occurrence
    248  * of newline character.
    249  *
    250  * @param indev  Input character device.
    251  * @param buf    Buffer where to store string terminated by NULL.
    252  * @param buflen Size of the buffer.
    253  *
    254  * @return Number of characters read.
    255  *
    256  */
    257 size_t gets(indev_t *indev, char *buf, size_t buflen)
    258 {
    259         size_t offset = 0;
    260         size_t count = 0;
    261         buf[offset] = 0;
    262 
    263         char32_t ch;
    264         while ((ch = indev_pop_character(indev)) != '\n') {
    265                 if (ch == '\b') {
    266                         if (count > 0) {
    267                                 /* Space, backspace, space */
    268                                 putuchar('\b');
    269                                 putuchar(' ');
    270                                 putuchar('\b');
    271 
    272                                 count--;
    273                                 offset = str_lsize(buf, count);
    274                                 buf[offset] = 0;
    275                         }
    276                 }
    277 
    278                 if (chr_encode(ch, buf, &offset, buflen - 1) == EOK) {
    279                         putuchar(ch);
    280                         count++;
    281                         buf[offset] = 0;
    282                 }
    283         }
    284 
    285         return count;
    286 }
    287 
    288 /** Get character from input device & echo it to screen */
    289 char32_t getc(indev_t *indev)
    290 {
    291         char32_t ch = indev_pop_character(indev);
    292         putuchar(ch);
    293         return ch;
    294 }
    295 
    296226void kio_update(void *event)
    297227{
     
    299229                return;
    300230
    301         spinlock_lock(&kio_lock);
    302 
    303         if (kio_uspace > 0) {
    304                 if (event_notify_3(EVENT_KIO, true, kio_start, kio_len,
    305                     kio_uspace) == EOK)
    306                         kio_uspace = 0;
    307         }
    308 
    309         spinlock_unlock(&kio_lock);
     231        irq_spinlock_lock(&kio_lock, true);
     232
     233        if (kio_notified != kio_written) {
     234                if (event_notify_1(EVENT_KIO, true, kio_written) == EOK)
     235                        kio_notified = kio_written;
     236        }
     237
     238        irq_spinlock_unlock(&kio_lock, true);
    310239}
    311240
     
    320249                return;
    321250
    322         spinlock_lock(&kio_lock);
     251        irq_spinlock_lock(&kio_lock, true);
     252
     253        if (!irq_spinlock_trylock(&flush_lock)) {
     254                /* Someone is currently flushing. */
     255                irq_spinlock_unlock(&kio_lock, true);
     256                return;
     257        }
     258
     259        /* A small-ish local buffer so that we can write to output in chunks. */
     260        char buffer[256];
    323261
    324262        /* Print characters that weren't printed earlier */
    325         while (kio_stored > 0) {
    326                 char32_t tmp = kio[(kio_start + kio_len - kio_stored) % KIO_LENGTH];
    327                 kio_stored--;
     263        while (kio_written != kio_processed) {
     264                size_t offset = kio_processed % KIO_LENGTH;
     265                size_t len = min(kio_written - kio_processed, KIO_LENGTH - offset);
     266                len = min(len, sizeof(buffer));
     267
     268                /* Take out a chunk of the big buffer. */
     269                memcpy(buffer, &kio[offset], len);
     270                kio_processed += len;
    328271
    329272                /*
    330                  * We need to give up the spinlock for
    331                  * the physical operation of writing out
    332                  * the character.
     273                 * We need to give up the spinlock for the physical operation of writing
     274                 * out the buffer.
    333275                 */
    334                 spinlock_unlock(&kio_lock);
    335                 stdout->op->write(stdout, tmp);
    336                 spinlock_lock(&kio_lock);
    337         }
    338 
    339         spinlock_unlock(&kio_lock);
    340 }
    341 
    342 /** Put a character into the output buffer.
    343  *
    344  * The caller is required to hold kio_lock
    345  */
    346 void kio_push_char(const char32_t ch)
    347 {
    348         kio[(kio_start + kio_len) % KIO_LENGTH] = ch;
    349         if (kio_len < KIO_LENGTH)
    350                 kio_len++;
    351         else
    352                 kio_start = (kio_start + 1) % KIO_LENGTH;
    353 
    354         if (kio_stored < kio_len)
    355                 kio_stored++;
    356 
    357         /* The character is stored for uspace */
    358         if (kio_uspace < kio_len)
    359                 kio_uspace++;
    360 }
    361 
    362 void putuchar(const char32_t ch)
     276                irq_spinlock_unlock(&kio_lock, true);
     277                stdout->op->write(stdout, buffer, len);
     278                irq_spinlock_lock(&kio_lock, true);
     279        }
     280
     281        irq_spinlock_unlock(&flush_lock, false);
     282        irq_spinlock_unlock(&kio_lock, true);
     283}
     284
     285void kio_push_bytes(const char *s, size_t n)
     286{
     287        /* Skip the section we know we can't keep. */
     288        if (n > KIO_LENGTH) {
     289                size_t lost = n - KIO_LENGTH;
     290                kio_written += lost;
     291                s += lost;
     292                n -= lost;
     293        }
     294
     295        size_t offset = kio_written % KIO_LENGTH;
     296        if (offset + n > KIO_LENGTH) {
     297                size_t first = KIO_LENGTH - offset;
     298                size_t last = n - first;
     299                memcpy(kio + offset, s, first);
     300                memcpy(kio, s + first, last);
     301        } else {
     302                memcpy(kio + offset, s, n);
     303        }
     304
     305        kio_written += n;
     306}
     307
     308static void early_putstr(const char *s, size_t n)
     309{
     310        irq_spinlock_lock(&early_mbstate_lock, true);
     311
     312        size_t offset = 0;
     313        char32_t c;
     314
     315        while ((c = str_decode_r(s, &offset, n, U_SPECIAL, &early_mbstate)))
     316                early_putuchar(c);
     317
     318        irq_spinlock_unlock(&early_mbstate_lock, true);
     319}
     320
     321void putstr(const char *s, size_t n)
    363322{
    364323        bool ordy = ((stdout) && (stdout->op->write));
    365324
    366         spinlock_lock(&kio_lock);
    367         kio_push_char(ch);
    368         spinlock_unlock(&kio_lock);
     325        irq_spinlock_lock(&kio_lock, true);
     326        kio_push_bytes(s, n);
     327        irq_spinlock_unlock(&kio_lock, true);
    369328
    370329        /* Output stored characters */
     
    379338                 * The early_putuchar() function is used to output
    380339                 * the character for low-level debugging purposes.
    381                  * Note that the early_putc() function might be
     340                 * Note that the early_putuchar() function might be
    382341                 * a no-op on certain hardware configurations.
    383342                 */
    384                 early_putuchar(ch);
    385         }
    386 
    387         /* Force notification on newline */
    388         if (ch == '\n')
     343                early_putstr(s, n);
     344        }
     345
     346        /* Force notification when containing a newline */
     347        if (memchr(s, '\n', n) != NULL)
    389348                kio_update(NULL);
     349}
     350
     351/** Reads up to `size` characters from kio buffer starting at character `at`.
     352 *
     353 * @param size  Maximum number of characters that can be stored in buffer.
     354 *              Values greater than KIO_LENGTH are silently treated as KIO_LENGTH
     355 *              for the purposes of calculating the return value.
     356 * @return Number of characters read. Can be more than `size`.
     357 *         In that case, `size` characters are written to user buffer
     358 *         and the extra amount is the number of characters missed.
     359 */
     360sysarg_t sys_kio_read(uspace_addr_t buf, size_t size, size_t at)
     361{
     362        errno_t rc;
     363        size_t missed = 0;
     364
     365        irq_spinlock_lock(&kio_lock, true);
     366
     367        if (at == kio_written) {
     368                irq_spinlock_unlock(&kio_lock, true);
     369                return 0;
     370        }
     371
     372        size_t readable_chars = kio_written - at;
     373        if (readable_chars > KIO_LENGTH) {
     374                missed = readable_chars - KIO_LENGTH;
     375                readable_chars = KIO_LENGTH;
     376        }
     377
     378        size_t actual_read = min(readable_chars, size);
     379        size_t offset = (kio_written - readable_chars) % KIO_LENGTH;
     380
     381        if (offset + actual_read > KIO_LENGTH) {
     382                size_t first = KIO_LENGTH - offset;
     383                size_t last = actual_read - first;
     384
     385                rc = copy_to_uspace(buf, &kio[offset], first);
     386                if (rc == EOK)
     387                        rc = copy_to_uspace(buf + first, &kio[0], last);
     388        } else {
     389                rc = copy_to_uspace(buf, &kio[offset], actual_read);
     390        }
     391
     392        irq_spinlock_unlock(&kio_lock, true);
     393
     394        if (rc != EOK) {
     395                log(LF_OTHER, LVL_WARN,
     396                    "[%s(%" PRIu64 ")] Terminating due to invalid memory buffer"
     397                    " in SYS_KIO_READ.\n", TASK->name, TASK->taskid);
     398                task_kill_self(true);
     399        }
     400
     401        return actual_read + missed;
    390402}
    391403
     
    425437                }
    426438                data[size] = 0;
     439
     440                uint8_t substitute = '\x1a';
     441                str_sanitize(data, size, substitute);
    427442
    428443                switch (cmd) {
     
    446461}
    447462
     463/** Lock console output, ensuring that lines from different threads don't
     464 * interleave. Does nothing when preemption is disabled, so that debugging
     465 * and error printouts in sensitive areas still work.
     466 */
     467void console_lock(void)
     468{
     469        if (!PREEMPTION_DISABLED)
     470                mutex_lock(&console_mutex);
     471}
     472
     473/** Unlocks console output. See console_lock()
     474 */
     475void console_unlock(void)
     476{
     477        if (!PREEMPTION_DISABLED)
     478                mutex_unlock(&console_mutex);
     479}
     480
    448481/** @}
    449482 */
Note: See TracChangeset for help on using the changeset viewer.