Changeset 29e7cc7 in mainline for kernel/generic/src/console/console.c


Ignore:
Timestamp:
2025-04-18T15:14:10Z (3 months ago)
Author:
Miroslav Cimerman <mc@…>
Children:
e77c3ed
Parents:
800d188 (diff), 25fdb2d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'upstream/master' into helenraid

File:
1 edited

Legend:

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

    r800d188 r29e7cc7  
    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);
     
    248229                return;
    249230
    250         spinlock_lock(&kio_lock);
    251 
    252         if (kio_uspace > 0) {
    253                 if (event_notify_3(EVENT_KIO, true, kio_start, kio_len,
    254                     kio_uspace) == EOK)
    255                         kio_uspace = 0;
    256         }
    257 
    258         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);
    259239}
    260240
     
    269249                return;
    270250
    271         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];
    272261
    273262        /* Print characters that weren't printed earlier */
    274         while (kio_stored > 0) {
    275                 char32_t tmp = kio[(kio_start + kio_len - kio_stored) % KIO_LENGTH];
    276                 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;
    277271
    278272                /*
    279                  * We need to give up the spinlock for
    280                  * the physical operation of writing out
    281                  * the character.
     273                 * We need to give up the spinlock for the physical operation of writing
     274                 * out the buffer.
    282275                 */
    283                 spinlock_unlock(&kio_lock);
    284                 stdout->op->write(stdout, tmp);
    285                 spinlock_lock(&kio_lock);
    286         }
    287 
    288         spinlock_unlock(&kio_lock);
    289 }
    290 
    291 /** Put a character into the output buffer.
    292  *
    293  * The caller is required to hold kio_lock
    294  */
    295 void kio_push_char(const char32_t ch)
    296 {
    297         kio[(kio_start + kio_len) % KIO_LENGTH] = ch;
    298         if (kio_len < KIO_LENGTH)
    299                 kio_len++;
    300         else
    301                 kio_start = (kio_start + 1) % KIO_LENGTH;
    302 
    303         if (kio_stored < kio_len)
    304                 kio_stored++;
    305 
    306         /* The character is stored for uspace */
    307         if (kio_uspace < kio_len)
    308                 kio_uspace++;
    309 }
    310 
    311 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)
    312322{
    313323        bool ordy = ((stdout) && (stdout->op->write));
    314324
    315         spinlock_lock(&kio_lock);
    316         kio_push_char(ch);
    317         spinlock_unlock(&kio_lock);
     325        irq_spinlock_lock(&kio_lock, true);
     326        kio_push_bytes(s, n);
     327        irq_spinlock_unlock(&kio_lock, true);
    318328
    319329        /* Output stored characters */
     
    331341                 * a no-op on certain hardware configurations.
    332342                 */
    333                 early_putuchar(ch);
    334         }
    335 
    336         /* Force notification on newline */
    337         if (ch == '\n')
     343                early_putstr(s, n);
     344        }
     345
     346        /* Force notification when containing a newline */
     347        if (memchr(s, '\n', n) != NULL)
    338348                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;
    339402}
    340403
     
    374437                }
    375438                data[size] = 0;
     439
     440                uint8_t substitute = '\x1a';
     441                str_sanitize(data, size, substitute);
    376442
    377443                switch (cmd) {
     
    395461}
    396462
     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
    397481/** @}
    398482 */
Note: See TracChangeset for help on using the changeset viewer.