source: mainline/kernel/generic/src/console/console.c@ d5b37b6

Last change on this file since d5b37b6 was d5b37b6, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 2 months ago

Use a new syscall, SYS_KIO_READ, for reading from KIO buffer

Originally, the buffer memory was shared between kernel and
uspace, presumably to avoid the overhead of syscalls.
However, this makes synchronization with kernel impossible,
so it is not possible to ensure it works reliably without
random glitches. Also, relative to everything else /app/kio
does with the data, the syscall overhead is positively tiny.

  • Property mode set to 100644
File size: 10.4 KB
Line 
1/*
2 * Copyright (c) 2003 Josef Cejka
3 * Copyright (c) 2005 Jakub Jermar
4 * Copyright (c) 2025 Jiří Zárevúcky
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup kernel_generic_console
32 * @{
33 */
34/** @file
35 */
36
37#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 <panic.h>
43#include <preemption.h>
44#include <proc/task.h>
45#include <putchar.h>
46#include <stdatomic.h>
47#include <stdio.h>
48#include <stdlib.h> /* malloc */
49#include <synch/mutex.h>
50#include <synch/spinlock.h>
51#include <syscall/copy.h>
52#include <sysinfo/sysinfo.h>
53
54#define KIO_PAGES 8
55#define KIO_LENGTH (KIO_PAGES * PAGE_SIZE / sizeof(char32_t))
56
57/** Kernel log cyclic buffer */
58static char32_t kio[KIO_LENGTH];
59
60/** Kernel log initialized */
61static atomic_bool kio_inited = ATOMIC_VAR_INIT(false);
62
63/** A mutex for preventing interleaving of output lines from different threads.
64 * May not be held in some circumstances, so locking of any internal shared
65 * structures is still necessary.
66 */
67static MUTEX_INITIALIZE(console_mutex, MUTEX_RECURSIVE);
68
69/** Number of characters written to buffer. Periodically overflows. */
70static size_t kio_written = 0;
71
72/** Number of characters written to output devices. Periodically overflows. */
73static size_t kio_processed = 0;
74
75/** Last notification sent to uspace. */
76static size_t kio_notified = 0;
77
78/** Kernel log spinlock */
79IRQ_SPINLOCK_INITIALIZE(kio_lock);
80
81static indev_t stdin_sink;
82static outdev_t stdout_source;
83
84static void stdin_signal(indev_t *, indev_signal_t);
85
86static indev_operations_t stdin_ops = {
87 .poll = NULL,
88 .signal = stdin_signal
89};
90
91static void stdout_write(outdev_t *, char32_t);
92static void stdout_redraw(outdev_t *);
93static void stdout_scroll_up(outdev_t *);
94static void stdout_scroll_down(outdev_t *);
95
96static outdev_operations_t stdout_ops = {
97 .write = stdout_write,
98 .redraw = stdout_redraw,
99 .scroll_up = stdout_scroll_up,
100 .scroll_down = stdout_scroll_down
101};
102
103/** Override kernel console lockout */
104bool console_override = false;
105
106/** Standard input and output character devices */
107indev_t *stdin = NULL;
108outdev_t *stdout = NULL;
109
110indev_t *stdin_wire(void)
111{
112 if (stdin == NULL) {
113 indev_initialize("stdin", &stdin_sink, &stdin_ops);
114 stdin = &stdin_sink;
115 }
116
117 return stdin;
118}
119
120static void stdin_signal(indev_t *indev, indev_signal_t signal)
121{
122 switch (signal) {
123 case INDEV_SIGNAL_SCROLL_UP:
124 if (stdout != NULL)
125 stdout_scroll_up(stdout);
126 break;
127 case INDEV_SIGNAL_SCROLL_DOWN:
128 if (stdout != NULL)
129 stdout_scroll_down(stdout);
130 break;
131 }
132}
133
134void stdout_wire(outdev_t *outdev)
135{
136 if (stdout == NULL) {
137 outdev_initialize("stdout", &stdout_source, &stdout_ops);
138 stdout = &stdout_source;
139 }
140
141 list_append(&outdev->link, &stdout->list);
142}
143
144static void stdout_write(outdev_t *dev, char32_t ch)
145{
146 list_foreach(dev->list, link, outdev_t, sink) {
147 if ((sink) && (sink->op->write))
148 sink->op->write(sink, ch);
149 }
150}
151
152static void stdout_redraw(outdev_t *dev)
153{
154 list_foreach(dev->list, link, outdev_t, sink) {
155 if ((sink) && (sink->op->redraw))
156 sink->op->redraw(sink);
157 }
158}
159
160static void stdout_scroll_up(outdev_t *dev)
161{
162 list_foreach(dev->list, link, outdev_t, sink) {
163 if ((sink) && (sink->op->scroll_up))
164 sink->op->scroll_up(sink);
165 }
166}
167
168static void stdout_scroll_down(outdev_t *dev)
169{
170 list_foreach(dev->list, link, outdev_t, sink) {
171 if ((sink) && (sink->op->scroll_down))
172 sink->op->scroll_down(sink);
173 }
174}
175
176/** Initialize kernel logging facility
177 */
178void kio_init(void)
179{
180 event_set_unmask_callback(EVENT_KIO, kio_update);
181 atomic_store(&kio_inited, true);
182}
183
184void grab_console(void)
185{
186 sysinfo_set_item_val("kconsole", NULL, true);
187 event_notify_1(EVENT_KCONSOLE, false, true);
188 bool prev = console_override;
189
190 console_override = true;
191 if ((stdout) && (stdout->op->redraw))
192 stdout->op->redraw(stdout);
193
194 if ((stdin) && (!prev)) {
195 /*
196 * Force the console to print the prompt.
197 */
198 indev_push_character(stdin, '\n');
199 }
200}
201
202void release_console(void)
203{
204 sysinfo_set_item_val("kconsole", NULL, false);
205 console_override = false;
206 event_notify_1(EVENT_KCONSOLE, false, false);
207}
208
209/** Activate kernel console override */
210sysarg_t sys_debug_console(void)
211{
212#ifdef CONFIG_KCONSOLE
213 grab_console();
214 return true;
215#else
216 return false;
217#endif
218}
219
220void kio_update(void *event)
221{
222 if (!atomic_load(&kio_inited))
223 return;
224
225 irq_spinlock_lock(&kio_lock, true);
226
227 if (kio_notified != kio_written) {
228 if (event_notify_1(EVENT_KIO, true, kio_written) == EOK)
229 kio_notified = kio_written;
230 }
231
232 irq_spinlock_unlock(&kio_lock, true);
233}
234
235/** Flush characters that are stored in the output buffer
236 *
237 */
238void kio_flush(void)
239{
240 bool ordy = ((stdout) && (stdout->op->write));
241
242 if (!ordy)
243 return;
244
245 irq_spinlock_lock(&kio_lock, true);
246
247 /* Print characters that weren't printed earlier */
248 while (kio_written != kio_processed) {
249 char32_t tmp = kio[kio_processed % KIO_LENGTH];
250 kio_processed++;
251
252 /*
253 * We need to give up the spinlock for
254 * the physical operation of writing out
255 * the character.
256 */
257 irq_spinlock_unlock(&kio_lock, true);
258 stdout->op->write(stdout, tmp);
259 irq_spinlock_lock(&kio_lock, true);
260 }
261
262 irq_spinlock_unlock(&kio_lock, true);
263}
264
265/** Put a character into the output buffer.
266 *
267 * The caller is required to hold kio_lock
268 */
269void kio_push_char(const char32_t ch)
270{
271 kio[kio_written % KIO_LENGTH] = ch;
272 kio_written++;
273}
274
275void putuchar(const char32_t ch)
276{
277 bool ordy = ((stdout) && (stdout->op->write));
278
279 irq_spinlock_lock(&kio_lock, true);
280 kio_push_char(ch);
281 irq_spinlock_unlock(&kio_lock, true);
282
283 /* Output stored characters */
284 kio_flush();
285
286 if (!ordy) {
287 /*
288 * No standard output routine defined yet.
289 * The character is still stored in the kernel log
290 * for possible future output.
291 *
292 * The early_putuchar() function is used to output
293 * the character for low-level debugging purposes.
294 * Note that the early_putuchar() function might be
295 * a no-op on certain hardware configurations.
296 */
297 early_putuchar(ch);
298 }
299
300 /* Force notification on newline */
301 if (ch == '\n')
302 kio_update(NULL);
303}
304
305/** Reads up to `size` characters from kio buffer starting at character `at`.
306 *
307 * @param size Maximum number of characters that can be stored in buffer.
308 * Values greater than KIO_LENGTH are silently treated as KIO_LENGTH
309 * for the purposes of calculating the return value.
310 * @return Number of characters read. Can be more than `size`.
311 * In that case, `size` characters are written to user buffer
312 * and the extra amount is the number of characters missed.
313 */
314sysarg_t sys_kio_read(uspace_addr_t buf, size_t size, size_t at)
315{
316 errno_t rc;
317 size_t missed = 0;
318
319 irq_spinlock_lock(&kio_lock, true);
320
321 if (at == kio_written) {
322 irq_spinlock_unlock(&kio_lock, true);
323 return 0;
324 }
325
326 size_t readable_chars = kio_written - at;
327 if (readable_chars > KIO_LENGTH) {
328 missed = readable_chars - KIO_LENGTH;
329 readable_chars = KIO_LENGTH;
330 }
331
332 size_t actual_read = min(readable_chars, size);
333 size_t offset = (kio_written - readable_chars) % KIO_LENGTH;
334
335 if (offset + actual_read > KIO_LENGTH) {
336 size_t first = KIO_LENGTH - offset;
337 size_t last = actual_read - first;
338 size_t first_bytes = first * sizeof(kio[0]);
339 size_t last_bytes = last * sizeof(kio[0]);
340
341 rc = copy_to_uspace(buf, &kio[offset], first_bytes);
342 if (rc == EOK)
343 rc = copy_to_uspace(buf + first_bytes, &kio[0], last_bytes);
344 } else {
345 rc = copy_to_uspace(buf, &kio[offset], actual_read * sizeof(kio[0]));
346 }
347
348 irq_spinlock_unlock(&kio_lock, true);
349
350 if (rc != EOK) {
351 log(LF_OTHER, LVL_WARN,
352 "[%s(%" PRIu64 ")] Terminating due to invalid memory buffer"
353 " in SYS_KIO_READ.\n", TASK->name, TASK->taskid);
354 task_kill_self(true);
355 }
356
357 return actual_read + missed;
358}
359
360/** Print using kernel facility
361 *
362 * Print to kernel log.
363 *
364 */
365sys_errno_t sys_kio(int cmd, uspace_addr_t buf, size_t size)
366{
367 char *data;
368 errno_t rc;
369
370 switch (cmd) {
371 case KIO_UPDATE:
372 kio_update(NULL);
373 return EOK;
374 case KIO_WRITE:
375 case KIO_COMMAND:
376 break;
377 default:
378 return ENOTSUP;
379 }
380
381 if (size > PAGE_SIZE)
382 return (sys_errno_t) ELIMIT;
383
384 if (size > 0) {
385 data = (char *) malloc(size + 1);
386 if (!data)
387 return (sys_errno_t) ENOMEM;
388
389 rc = copy_from_uspace(data, buf, size);
390 if (rc) {
391 free(data);
392 return (sys_errno_t) rc;
393 }
394 data[size] = 0;
395
396 uint8_t substitute = '\x1a';
397 str_sanitize(data, size, substitute);
398
399 switch (cmd) {
400 case KIO_WRITE:
401 printf("[%s(%lu)] %s\n", TASK->name,
402 (unsigned long) TASK->taskid, data);
403 break;
404 case KIO_COMMAND:
405 if (!stdin)
406 break;
407 for (unsigned int i = 0; i < size; i++)
408 indev_push_character(stdin, data[i]);
409 indev_push_character(stdin, '\n');
410 break;
411 }
412
413 free(data);
414 }
415
416 return EOK;
417}
418
419/** Lock console output, ensuring that lines from different threads don't
420 * interleave. Does nothing when preemption is disabled, so that debugging
421 * and error printouts in sensitive areas still work.
422 */
423void console_lock(void)
424{
425 if (!PREEMPTION_DISABLED)
426 mutex_lock(&console_mutex);
427}
428
429/** Unlocks console output. See console_lock()
430 */
431void console_unlock(void)
432{
433 if (!PREEMPTION_DISABLED)
434 mutex_unlock(&console_mutex);
435}
436
437/** @}
438 */
Note: See TracBrowser for help on using the repository browser.