Ignore:
File:
1 edited

Legend:

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

    r39e1b9a r63ed840  
    22 * Copyright (c) 2003 Josef Cejka
    33 * Copyright (c) 2005 Jakub Jermar
    4  * Copyright (c) 2025 Jiří Zárevúcky
    54 * All rights reserved.
    65 *
     
    3534 */
    3635
     36#include <assert.h>
     37#include <console/console.h>
     38#include <console/chardev.h>
     39#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>
    3756#include <abi/kio.h>
    38 #include <console/chardev.h>
    39 #include <console/console.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>
     57#include <mm/frame.h> /* SIZE2FRAMES */
    4858#include <stdlib.h>  /* malloc */
    49 #include <str.h>
    50 #include <synch/mutex.h>
    51 #include <synch/spinlock.h>
    52 #include <syscall/copy.h>
    53 #include <sysinfo/sysinfo.h>
    5459
    5560#define KIO_PAGES    8
    56 #define KIO_LENGTH   (KIO_PAGES * PAGE_SIZE)
     61#define KIO_LENGTH   (KIO_PAGES * PAGE_SIZE / sizeof(char32_t))
    5762
    5863/** Kernel log cyclic buffer */
    59 static char kio[KIO_LENGTH];
     64char32_t kio[KIO_LENGTH] __attribute__((aligned(PAGE_SIZE)));
    6065
    6166/** Kernel log initialized */
    6267static atomic_bool kio_inited = ATOMIC_VAR_INIT(false);
    6368
    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  */
    68 static MUTEX_INITIALIZE(console_mutex, MUTEX_RECURSIVE);
    69 
    70 /** Number of characters written to buffer. Periodically overflows. */
    71 static size_t kio_written = 0;
    72 
    73 /** Number of characters written to output devices. Periodically overflows. */
    74 static size_t kio_processed = 0;
    75 
    76 /** Last notification sent to uspace. */
    77 static size_t kio_notified = 0;
     69/** First kernel log characters */
     70static size_t kio_start = 0;
     71
     72/** Number of valid kernel log characters */
     73static size_t kio_len = 0;
     74
     75/** Number of stored (not printed) kernel log characters */
     76static size_t kio_stored = 0;
     77
     78/** Number of stored kernel log characters for uspace */
     79static size_t kio_uspace = 0;
    7880
    7981/** Kernel log spinlock */
    80 IRQ_SPINLOCK_INITIALIZE(kio_lock);
    81 
    82 static IRQ_SPINLOCK_INITIALIZE(flush_lock);
    83 
    84 static IRQ_SPINLOCK_INITIALIZE(early_mbstate_lock);
    85 static mbstate_t early_mbstate;
     82SPINLOCK_INITIALIZE_NAME(kio_lock, "kio_lock");
     83
     84/** Physical memory area used for kio buffer */
     85static parea_t kio_parea;
    8686
    8787static indev_t stdin_sink;
     
    9595};
    9696
    97 static void stdout_write(outdev_t *, const char *, size_t);
     97static void stdout_write(outdev_t *, char32_t);
    9898static void stdout_redraw(outdev_t *);
    9999static void stdout_scroll_up(outdev_t *);
     
    148148}
    149149
    150 static void stdout_write(outdev_t *dev, const char *s, size_t n)
     150static void stdout_write(outdev_t *dev, char32_t ch)
    151151{
    152152        list_foreach(dev->list, link, outdev_t, sink) {
    153153                if ((sink) && (sink->op->write))
    154                         sink->op->write(sink, s, n);
     154                        sink->op->write(sink, ch);
    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 *
    183188 */
    184189void kio_init(void)
    185190{
     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
    186205        event_set_unmask_callback(EVENT_KIO, kio_update);
    187206        atomic_store(&kio_inited, true);
     
    229248                return;
    230249
    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);
     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);
    239259}
    240260
     
    249269                return;
    250270
    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];
     271        spinlock_lock(&kio_lock);
    261272
    262273        /* Print characters that weren't printed earlier */
    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;
     274        while (kio_stored > 0) {
     275                char32_t tmp = kio[(kio_start + kio_len - kio_stored) % KIO_LENGTH];
     276                kio_stored--;
    271277
    272278                /*
    273                  * We need to give up the spinlock for the physical operation of writing
    274                  * out the buffer.
     279                 * We need to give up the spinlock for
     280                 * the physical operation of writing out
     281                 * the character.
    275282                 */
    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 
    285 void 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 
    308 static 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 
    321 void putstr(const char *s, size_t n)
     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 */
     295void 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
     311void putuchar(const char32_t ch)
    322312{
    323313        bool ordy = ((stdout) && (stdout->op->write));
    324314
    325         irq_spinlock_lock(&kio_lock, true);
    326         kio_push_bytes(s, n);
    327         irq_spinlock_unlock(&kio_lock, true);
     315        spinlock_lock(&kio_lock);
     316        kio_push_char(ch);
     317        spinlock_unlock(&kio_lock);
    328318
    329319        /* Output stored characters */
     
    341331                 * a no-op on certain hardware configurations.
    342332                 */
    343                 early_putstr(s, n);
    344         }
    345 
    346         /* Force notification when containing a newline */
    347         if (memchr(s, '\n', n) != NULL)
     333                early_putuchar(ch);
     334        }
     335
     336        /* Force notification on newline */
     337        if (ch == '\n')
    348338                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  */
    360 sysarg_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;
    402339}
    403340
     
    437374                }
    438375                data[size] = 0;
    439 
    440                 uint8_t substitute = '\x1a';
    441                 str_sanitize(data, size, substitute);
    442376
    443377                switch (cmd) {
     
    461395}
    462396
    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  */
    467 void console_lock(void)
    468 {
    469         if (!PREEMPTION_DISABLED)
    470                 mutex_lock(&console_mutex);
    471 }
    472 
    473 /** Unlocks console output. See console_lock()
    474  */
    475 void console_unlock(void)
    476 {
    477         if (!PREEMPTION_DISABLED)
    478                 mutex_unlock(&console_mutex);
    479 }
    480 
    481397/** @}
    482398 */
Note: See TracChangeset for help on using the changeset viewer.