From a1b41f955c3082ac6b05f578e2d9aa9e427a2271 Mon Sep 17 00:00:00 2001
From: Ian Moffett <ian@osmora.org>
Date: Thu, 23 May 2024 21:42:24 -0400
Subject: kernel: syslog: Add kernel message buffer

This commit introduces the kernel message buffer and makes system
messages no longer be written to the TTY after kernel init. The kernel
message buffer can be read from /proc/kmsg

Signed-off-by: Ian Moffett <ian@osmora.org>
---
 sys/arch/amd64/conf/GENERIC |  1 +
 sys/fs/procfs_subr.c        |  2 ++
 sys/include/sys/syslog.h    |  3 +++
 sys/kern/init_main.c        |  3 +++
 sys/kern/kern_syslog.c      | 50 ++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index b4fb29d..032519d 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -6,3 +6,4 @@ option PANIC_BEEP           yes
 // Kernel constants
 setval PANIC_BEEP_HZ        1050
 setval SCHED_NQUEUE         4
+setval KMSG_BUF_SHIFT       16
diff --git a/sys/fs/procfs_subr.c b/sys/fs/procfs_subr.c
index c1d77ba..8b95919 100644
--- a/sys/fs/procfs_subr.c
+++ b/sys/fs/procfs_subr.c
@@ -29,6 +29,7 @@
 
 #include <sys/panic.h>
 #include <sys/intr.h>
+#include <sys/syslog.h>
 #include <machine/cpu.h>
 #include <fs/procfs.h>
 #include <vm/vm.h>
@@ -109,4 +110,5 @@ procfs_populate(void)
     procfs_add_entry("memstat", memstat);
 
     intr_init_proc();
+    syslog_init_proc();
 }
diff --git a/sys/include/sys/syslog.h b/sys/include/sys/syslog.h
index 3015f6b..12febbc 100644
--- a/sys/include/sys/syslog.h
+++ b/sys/include/sys/syslog.h
@@ -34,6 +34,7 @@
 #ifndef _SYS_SYSLOG_H_
 #define _SYS_SYSLOG_H_
 
+#include <sys/types.h>
 #include <stdarg.h>
 #include <dev/vcons/vcons.h>
 
@@ -56,10 +57,12 @@
     kprintf(__VA_ARGS__);
 
 void syslog_init(void);
+void syslog_init_proc(void);
 void kprintf(const char *fmt, ...);
 void vkprintf(const char *fmt, va_list *ap);
 
 extern struct vcons_screen g_syslog_screen;
+extern bool g_syslog_use_tty;
 
 #endif  /* defined(_KERNEL) */
 
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index 10cd90e..5f5e8c3 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -102,6 +102,9 @@ main(void)
     sched_init();
     ci = this_cpu();
 
+    /* Stop writing kernel messages to TTY */
+    g_syslog_use_tty = false;
+
     __TRY_CALL(ap_bootstrap, ci);
     sched_enter();
 
diff --git a/sys/kern/kern_syslog.c b/sys/kern/kern_syslog.c
index 88ae8bc..712b69b 100644
--- a/sys/kern/kern_syslog.c
+++ b/sys/kern/kern_syslog.c
@@ -30,10 +30,46 @@
 #include <sys/syslog.h>
 #include <sys/machdep.h>
 #include <sys/tty.h>
+#include <sys/cdefs.h>
 #include <dev/vcons/vcons.h>
+#include <fs/procfs.h>
 #include <string.h>
 
+#if defined(__KMSG_BUF_SHIFT)
+#define KMSG_BUF_SHIFT __KMSG_BUF_SHIFT
+#else
+#define KMSG_BUF_SHIFT 12
+#endif
+
+#define KMSG_BUF_SIZE (1 << KMSG_BUF_SHIFT)
+
+__STATIC_ASSERT(KMSG_BUF_SHIFT <= 16, "Log buffer shift too large!\n");
+
+static char kmsg_buf[KMSG_BUF_SIZE];
+static size_t kmsg_buf_idx = 0;
+static struct proc_entry *kmsg_proc;
+
 struct vcons_screen g_syslog_screen = {0};
+bool g_syslog_use_tty = true;
+
+static inline void
+kmsg_buf_putc(char c)
+{
+    kmsg_buf[kmsg_buf_idx++] = c;
+    kmsg_buf[kmsg_buf_idx] = '\0';
+    if (kmsg_buf_idx >= (KMSG_BUF_SIZE - 1))
+        kmsg_buf_idx = 0;
+}
+
+static int
+proc_kmsg_read(struct proc_entry *p, struct sio_txn *sio)
+{
+    if (sio->len > KMSG_BUF_SIZE)
+        sio->len = KMSG_BUF_SIZE;
+
+    memcpy(sio->buf, kmsg_buf, sio->len);
+    return sio->len;
+}
 
 static void
 syslog_write(const char *s, size_t len)
@@ -45,7 +81,11 @@ syslog_write(const char *s, size_t len)
 #if defined(__SERIAL_DEBUG)
         serial_dbgch(*tmp_s);
 #endif  /* defined(__SERIAL_DEBUG) */
-        tty_putc(&g_root_tty, *tmp_s++, TTY_SOURCE_RAW);
+        kmsg_buf_putc(*tmp_s);
+        if (g_syslog_use_tty)
+            tty_putc(&g_root_tty, *tmp_s, TTY_SOURCE_RAW);
+
+        ++tmp_s;
     }
 
     tty_flush(&g_root_tty);
@@ -70,6 +110,14 @@ kprintf(const char *fmt, ...)
     va_end(ap);
 }
 
+void
+syslog_init_proc(void)
+{
+    kmsg_proc = procfs_alloc_entry();
+    kmsg_proc->read = proc_kmsg_read;
+    procfs_add_entry("kmsg", kmsg_proc);
+}
+
 void
 syslog_init(void)
 {
-- 
cgit v1.2.3