Ignore:
File:
1 edited

Legend:

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

    r39e1b9a rebb3538  
    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);
     
    224243}
    225244
     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 */
     257size_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 */
     289char32_t getc(indev_t *indev)
     290{
     291        char32_t ch = indev_pop_character(indev);
     292        putuchar(ch);
     293        return ch;
     294}
     295
    226296void kio_update(void *event)
    227297{
     
    229299                return;
    230300
    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);
     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);
    239310}
    240311
     
    249320                return;
    250321
    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];
     322        spinlock_lock(&kio_lock);
    261323
    262324        /* 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;
     325        while (kio_stored > 0) {
     326                char32_t tmp = kio[(kio_start + kio_len - kio_stored) % KIO_LENGTH];
     327                kio_stored--;
    271328
    272329                /*
    273                  * We need to give up the spinlock for the physical operation of writing
    274                  * out the buffer.
     330                 * We need to give up the spinlock for
     331                 * the physical operation of writing out
     332                 * the character.
    275333                 */
    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)
     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 */
     346void 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
     362void putuchar(const char32_t ch)
    322363{
    323364        bool ordy = ((stdout) && (stdout->op->write));
    324365
    325         irq_spinlock_lock(&kio_lock, true);
    326         kio_push_bytes(s, n);
    327         irq_spinlock_unlock(&kio_lock, true);
     366        spinlock_lock(&kio_lock);
     367        kio_push_char(ch);
     368        spinlock_unlock(&kio_lock);
    328369
    329370        /* Output stored characters */
     
    341382                 * a no-op on certain hardware configurations.
    342383                 */
    343                 early_putstr(s, n);
    344         }
    345 
    346         /* Force notification when containing a newline */
    347         if (memchr(s, '\n', n) != NULL)
     384                early_putuchar(ch);
     385        }
     386
     387        /* Force notification on newline */
     388        if (ch == '\n')
    348389                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;
    402390}
    403391
     
    437425                }
    438426                data[size] = 0;
    439 
    440                 uint8_t substitute = '\x1a';
    441                 str_sanitize(data, size, substitute);
    442427
    443428                switch (cmd) {
     
    461446}
    462447
    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 
    481448/** @}
    482449 */
Note: See TracChangeset for help on using the changeset viewer.