@@ -1,4 +1,4 @@
-drm-text-y := drm-text-buffer.o
+drm-text-y := drm-text-console.o drm-text-buffer.o
drm-text-$(CONFIG_DEBUG_FS) += drm-text-debugfs.o
obj-m += drm-text.o
@@ -308,7 +308,7 @@ static void drm_text_free(struct drm_text_buffer *text)
static int __init drm_text_init(void)
{
- int ret = 0;
+ int ret;
ret = drm_text_debugfs_init();
if (ret)
@@ -316,6 +316,7 @@ static int __init drm_text_init(void)
drm_text_scan_fbdev();
+ ret = drm_text_console_init();
return ret;
}
@@ -325,6 +326,7 @@ static void __exit drm_text_exit(void)
{
unsigned int i;
+ drm_text_console_exit();
drm_text_debugfs_exit();
for (i = 0; i < MAX_DRM_TEXT_BUFFERS; i++)
new file mode 100644
@@ -0,0 +1,205 @@
+#define DEBUG
+/*
+ * Copyright 2016 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/console.h>
+#include <linux/slab.h>
+
+#include "drm-text.h"
+
+static bool drm_text_console_panic;
+
+/**
+ * DOC: message/boot console
+ *
+ * The kernel message &console is enabled by using console=drm[n] where n is
+ * the primary minor number. Only one console is supported.
+ */
+
+static void drm_text_console_write(struct console *con, const char *str,
+ unsigned int num)
+{
+ struct drm_text_buffer *text;
+
+ drm_text_debug("%s(num=%u)\n", __func__, num);
+ if (drm_text_console_panic)
+ return;
+
+ text = drm_text_get(con->index);
+ if (!text)
+ return;
+
+ drm_text_write(text, str, num);
+ drm_text_flush(text, false);
+}
+
+static int drm_text_console_setup(struct console *con, char *options)
+{
+ struct drm_text_buffer *text;
+
+ drm_text_debug("%s[%u](%s) data=%p\n", __func__, con->index, options, con->data);
+
+ text = drm_text_get(con->index);
+ if (text)
+ drm_text_enable(text);
+
+ return 0;
+}
+
+/* TODO: test as a boot console */
+static struct console drm_text_console = {
+ .name = "drm",
+ .write = drm_text_console_write,
+ .setup = drm_text_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+/**
+ * DOC: panic console
+ *
+ * The panic &console is always enabled and collects kernel messages in a
+ * buffer as they come in.
+ * When the kernel does panic(), a panic notifier enables all text buffers.
+ * On the next &console->write it replays the message buffer and starts
+ * writing to all text buffers. Flushing of the text buffer to the pixel
+ * buffer is done inline instead of using the worker.
+ */
+
+#define DRM_TEXT_PANIC_KMSGS_MAX SZ_1K
+
+struct drm_text_panic_console {
+ char kmsgs[DRM_TEXT_PANIC_KMSGS_MAX];
+ size_t kmsg_pos;
+};
+
+static void drm_text_panic_write(const char *str, unsigned int num)
+{
+ unsigned int i;
+
+ drm_text_debug("%s(num=%u)\n", __func__, num);
+
+ for (i = 0; i < MAX_DRM_TEXT_BUFFERS; i++)
+ if (drm_text_buffers[i])
+ drm_text_write(drm_text_buffers[i], str, num);
+}
+
+static void drm_text_panic_flush(void)
+{
+ unsigned int i;
+
+ drm_text_debug("%s()\n", __func__);
+
+ for (i = 0; i < MAX_DRM_TEXT_BUFFERS; i++)
+ if (drm_text_buffers[i])
+ drm_text_flush(drm_text_buffers[i], true);
+}
+
+static void drm_text_panic_console_write(struct console *con, const char *str,
+ unsigned int num)
+{
+ struct drm_text_panic_console *pcon = con->data;
+ unsigned int i;
+
+ drm_text_debug("%s(num=%u)\n", __func__, num);
+
+ if (!pcon)
+ return;
+
+ /* Buffer up messages to be replayed on panic */
+ if (!drm_text_console_panic) {
+ for (i = 0; i < num; i++) {
+ pcon->kmsgs[pcon->kmsg_pos++] = *str++;
+ if (pcon->kmsg_pos == DRM_TEXT_PANIC_KMSGS_MAX)
+ pcon->kmsg_pos = 0;
+ }
+ return;
+ }
+
+ if (pcon->kmsgs[0]) {
+ /* replay messages, the first one might be partial */
+ if (pcon->kmsgs[pcon->kmsg_pos]) { /* buffer wrap around */
+ drm_text_panic_write(&pcon->kmsgs[pcon->kmsg_pos],
+ DRM_TEXT_PANIC_KMSGS_MAX - pcon->kmsg_pos);
+ drm_text_panic_write(pcon->kmsgs, pcon->kmsg_pos);
+ } else {
+ drm_text_panic_write(pcon->kmsgs, pcon->kmsg_pos);
+ }
+ pcon->kmsgs[0] = '\0';
+ }
+
+ drm_text_panic_write(str, num);
+ drm_text_panic_flush();
+}
+
+static struct console drm_text_panic_console = {
+ .name = "drmpanic",
+ .write = drm_text_panic_console_write,
+ .flags = CON_PRINTBUFFER | CON_ENABLED,
+ .index = 0,
+};
+
+static int drm_text_panic_console_setup(struct console *con)
+{
+ struct drm_text_panic_console *pcon;
+
+ drm_text_debug("%s[%u]() data=%p\n", __func__, con->index, con->data);
+
+ pcon = kzalloc(sizeof(*pcon), GFP_KERNEL);
+ if (!pcon)
+ return -ENOMEM;
+
+ con->data = pcon;
+
+ return 0;
+}
+
+int drm_text_panic(struct notifier_block *this, unsigned long ev, void *ptr)
+{
+ unsigned int i;
+
+ drm_text_console_panic = true;
+ for (i = 0; i < MAX_DRM_TEXT_BUFFERS; i++)
+ if (drm_text_buffers[i])
+ drm_text_enable(drm_text_buffers[i]);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block drm_text_panic_block = {
+ .notifier_call = drm_text_panic,
+};
+
+int drm_text_console_init(void)
+{
+ drm_text_log("%s\n", __func__);
+
+ register_console(&drm_text_panic_console);
+ drm_text_panic_console_setup(&drm_text_panic_console);
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &drm_text_panic_block);
+ drm_text_debug("drm_text_panic_console: index=%d, flags=0x%x\n",
+ drm_text_panic_console.index, drm_text_panic_console.flags);
+
+ register_console(&drm_text_console);
+ drm_text_debug("drm_text_console: index=%d, flags=0x%x\n",
+ drm_text_console.index, drm_text_console.flags);
+
+ return 0;
+}
+
+void drm_text_console_exit(void)
+{
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &drm_text_panic_block);
+ unregister_console(&drm_text_panic_console);
+ kfree(drm_text_panic_console.data);
+
+ unregister_console(&drm_text_console);
+}
@@ -173,6 +173,93 @@ static const struct file_operations drm_text_flush_ops = {
.llseek = default_llseek,
};
+/*
+ * Fake/simulate panic() at different levels:
+ * 1: only trigger panic handling internally
+ * 2: local_irq_disable()
+ * 3: bust_spinlocks();
+ * 100: don't fake it, do call panic()
+ */
+static int drm_text_fake_panic(unsigned int level)
+{
+/* The console_* symbols are not exported */
+// int old_loglevel = console_loglevel;
+
+ if (!level && level != 100 && level > 3)
+ return -EINVAL;
+
+ if (level == 100)
+ panic("TESTING");
+
+ if (level > 1)
+ local_irq_disable();
+
+// console_verbose();
+
+ if (level > 2)
+ bust_spinlocks(1);
+
+ pr_emerg("Kernel panic - not syncing: TESTING\n");
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+ dump_stack();
+#endif
+
+ /* simulate calling panic_notifier_list */
+ drm_text_panic(NULL, 0 , NULL);
+
+ if (level > 2)
+ bust_spinlocks(0);
+
+// console_flush_on_panic();
+
+ pr_emerg("---[ end Kernel panic - not syncing: TESTING\n");
+
+ if (level > 1)
+ local_irq_enable();
+
+// console_loglevel = old_loglevel;
+
+#ifdef HACK_NEED_FLUSHING
+{
+ struct drm_text_buffer *text = drm_text_get(0);
+
+ if (text && text->fb->funcs->dirty)
+ text->fb->funcs->dirty(text->fb, NULL, 0, 0, NULL, 0);
+}
+#endif
+ return 0;
+}
+
+static ssize_t drm_text_panic_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long long val;
+ char buf[24];
+ ssize_t ret = 0;
+ size_t size;
+
+ size = min(sizeof(buf) - 1, count);
+ if (copy_from_user(buf, user_buf, size))
+ return -EFAULT;
+
+ buf[size] = '\0';
+ ret = kstrtoull(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ ret = drm_text_fake_panic(1);
+
+ return ret < 0 ? ret : count;
+}
+
+static const struct file_operations drm_text_panic_ops = {
+ .write = drm_text_panic_write,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
int drm_text_debugfs_init(void)
{
drm_text_debugfs_root = debugfs_create_dir("drm-text", NULL);
@@ -189,6 +276,8 @@ int drm_text_debugfs_init(void)
debugfs_create_file("flush", S_IWUSR, drm_text_debugfs_root, NULL,
&drm_text_flush_ops);
+ debugfs_create_file("panic", S_IWUSR, drm_text_debugfs_root, NULL,
+ &drm_text_panic_ops);
return 0;
@@ -39,6 +39,10 @@ int drm_text_enable(struct drm_text_buffer *text);
int drm_text_disable(struct drm_text_buffer *text);
struct drm_text_buffer *drm_text_get(unsigned int index);
+int drm_text_console_init(void);
+void drm_text_console_exit(void);
+int drm_text_panic(struct notifier_block *this, unsigned long ev, void *ptr);
+
#ifdef DEBUG
#define drm_text_debug(fmt, ...) \
drm_text_log(fmt, ##__VA_ARGS__)
This adds support for panic and boot console. Signed-off-by: Noralf Trønnes <noralf@tronnes.org> --- drivers/gpu/drm/drm-text/Makefile | 2 +- drivers/gpu/drm/drm-text/drm-text-buffer.c | 4 +- drivers/gpu/drm/drm-text/drm-text-console.c | 205 ++++++++++++++++++++++++++++ drivers/gpu/drm/drm-text/drm-text-debugfs.c | 89 ++++++++++++ drivers/gpu/drm/drm-text/drm-text.h | 4 + 5 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/drm-text/drm-text-console.c