Index: kernel/generic/include/console/console.h
===================================================================
--- kernel/generic/include/console/console.h	(revision 571cc2dd327eec67cc03d4e0d937e24c3fdb6a17)
+++ kernel/generic/include/console/console.h	(revision d5b37b625566a35b56a2768799f8082882139e67)
@@ -36,6 +36,4 @@
 #define KERN_CONSOLE_H_
 
-#include <typedefs.h>
-#include <print.h>
 #include <console/chardev.h>
 #include <synch/spinlock.h>
@@ -69,4 +67,5 @@
 extern irq_spinlock_t kio_lock;
 
+extern sysarg_t sys_kio_read(uspace_addr_t buf, size_t size, size_t at);
 extern sys_errno_t sys_kio(int cmd, uspace_addr_t buf, size_t size);
 
Index: kernel/generic/src/console/console.c
===================================================================
--- kernel/generic/src/console/console.c	(revision 571cc2dd327eec67cc03d4e0d937e24c3fdb6a17)
+++ kernel/generic/src/console/console.c	(revision d5b37b625566a35b56a2768799f8082882139e67)
@@ -2,4 +2,5 @@
  * Copyright (c) 2003 Josef Cejka
  * Copyright (c) 2005 Jakub Jermar
+ * Copyright (c) 2025 Jiří Zárevúcky
  * All rights reserved.
  *
@@ -35,29 +36,19 @@
 
 #include <abi/kio.h>
-#include <arch.h>
-#include <assert.h>
-#include <atomic.h>
 #include <console/chardev.h>
 #include <console/console.h>
-#include <ddi/ddi.h>
-#include <ddi/irq.h>
 #include <errno.h>
 #include <ipc/event.h>
-#include <ipc/irq.h>
-#include <mm/frame.h> /* SIZE2FRAMES */
 #include <panic.h>
 #include <preemption.h>
-#include <proc/thread.h>
+#include <proc/task.h>
 #include <putchar.h>
 #include <stdatomic.h>
 #include <stdio.h>
 #include <stdlib.h>  /* malloc */
-#include <str.h>
 #include <synch/mutex.h>
 #include <synch/spinlock.h>
-#include <synch/waitq.h>
 #include <syscall/copy.h>
 #include <sysinfo/sysinfo.h>
-#include <typedefs.h>
 
 #define KIO_PAGES    8
@@ -65,5 +56,5 @@
 
 /** Kernel log cyclic buffer */
-char32_t kio[KIO_LENGTH] __attribute__((aligned(PAGE_SIZE)));
+static char32_t kio[KIO_LENGTH];
 
 /** Kernel log initialized */
@@ -87,7 +78,4 @@
 /** Kernel log spinlock */
 IRQ_SPINLOCK_INITIALIZE(kio_lock);
-
-/** Physical memory area used for kio buffer */
-static parea_t kio_parea;
 
 static indev_t stdin_sink;
@@ -187,26 +175,7 @@
 
 /** Initialize kernel logging facility
- *
- * The shared area contains kernel cyclic buffer. Userspace application may
- * be notified on new data with indication of position and size
- * of the data within the circular buffer.
- *
  */
 void kio_init(void)
 {
-	void *faddr = (void *) KA2PA(kio);
-
-	assert((uintptr_t) faddr % FRAME_SIZE == 0);
-
-	ddi_parea_init(&kio_parea);
-	kio_parea.pbase = (uintptr_t) faddr;
-	kio_parea.frames = SIZE2FRAMES(sizeof(kio));
-	kio_parea.unpriv = false;
-	kio_parea.mapped = false;
-	ddi_parea_register(&kio_parea);
-
-	sysinfo_set_item_val("kio.faddr", NULL, (sysarg_t) faddr);
-	sysinfo_set_item_val("kio.pages", NULL, KIO_PAGES);
-
 	event_set_unmask_callback(EVENT_KIO, kio_update);
 	atomic_store(&kio_inited, true);
@@ -332,4 +301,59 @@
 	if (ch == '\n')
 		kio_update(NULL);
+}
+
+/** Reads up to `size` characters from kio buffer starting at character `at`.
+ *
+ * @param size  Maximum number of characters that can be stored in buffer.
+ *              Values greater than KIO_LENGTH are silently treated as KIO_LENGTH
+ *              for the purposes of calculating the return value.
+ * @return Number of characters read. Can be more than `size`.
+ *         In that case, `size` characters are written to user buffer
+ *         and the extra amount is the number of characters missed.
+ */
+sysarg_t sys_kio_read(uspace_addr_t buf, size_t size, size_t at)
+{
+	errno_t rc;
+	size_t missed = 0;
+
+	irq_spinlock_lock(&kio_lock, true);
+
+	if (at == kio_written) {
+		irq_spinlock_unlock(&kio_lock, true);
+		return 0;
+	}
+
+	size_t readable_chars = kio_written - at;
+	if (readable_chars > KIO_LENGTH) {
+		missed = readable_chars - KIO_LENGTH;
+		readable_chars = KIO_LENGTH;
+	}
+
+	size_t actual_read = min(readable_chars, size);
+	size_t offset = (kio_written - readable_chars) % KIO_LENGTH;
+
+	if (offset + actual_read > KIO_LENGTH) {
+		size_t first = KIO_LENGTH - offset;
+		size_t last = actual_read - first;
+		size_t first_bytes = first * sizeof(kio[0]);
+		size_t last_bytes = last * sizeof(kio[0]);
+
+		rc = copy_to_uspace(buf, &kio[offset], first_bytes);
+		if (rc == EOK)
+			rc = copy_to_uspace(buf + first_bytes, &kio[0], last_bytes);
+	} else {
+		rc = copy_to_uspace(buf, &kio[offset], actual_read * sizeof(kio[0]));
+	}
+
+	irq_spinlock_unlock(&kio_lock, true);
+
+	if (rc != EOK) {
+		log(LF_OTHER, LVL_WARN,
+		    "[%s(%" PRIu64 ")] Terminating due to invalid memory buffer"
+		    " in SYS_KIO_READ.\n", TASK->name, TASK->taskid);
+		task_kill_self(true);
+	}
+
+	return actual_read + missed;
 }
 
Index: kernel/generic/src/syscall/syscall.c
===================================================================
--- kernel/generic/src/syscall/syscall.c	(revision 571cc2dd327eec67cc03d4e0d937e24c3fdb6a17)
+++ kernel/generic/src/syscall/syscall.c	(revision d5b37b625566a35b56a2768799f8082882139e67)
@@ -137,4 +137,5 @@
 
 	[SYS_KLOG] = (syshandler_t) sys_klog,
+	[SYS_KIO_READ] = (syshandler_t) sys_kio_read,
 };
 
