printk: Save console options for add_preferred_console_match()
authorTony Lindgren <tony@atomide.com>
Wed, 27 Mar 2024 10:59:35 +0000 (12:59 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 9 Apr 2024 13:30:13 +0000 (15:30 +0200)
Driver subsystems may need to translate the preferred console name to the
character device name used. We already do some of this in console_setup()
with a few hardcoded names, but that does not scale well.

The console options are parsed early in console_setup(), and the consoles
are added with __add_preferred_console(). At this point we don't know much
about the character device names and device drivers getting probed.

To allow driver subsystems to set up a preferred console, let's save the
kernel command line console options. To add a preferred console from a
driver subsystem with optional character device name translation, let's
add a new function add_preferred_console_match().

This allows the serial core layer to support console=DEVNAME:0.0 style
hardware based addressing in addition to the current console=ttyS0 style
naming. And we can start moving console_setup() character device parsing
to the driver subsystem specific code.

We use a separate array from the console_cmdline array as the character
device name and index may be unknown at the console_setup() time. And
eventually there's no need to call __add_preferred_console() until the
subsystem is ready to handle the console.

Adding the console name in addition to the character device name, and a
flag for an added console, could be added to the struct console_cmdline.
And the console_cmdline array handling could be modified accordingly. But
that complicates things compared saving the console options, and then
adding the consoles when the subsystems handling the consoles are ready.

Co-developed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20240327110021.59793-2-tony@atomide.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/printk.h
kernel/printk/Makefile
kernel/printk/conopt.c [new file with mode: 0644]
kernel/printk/console_cmdline.h
kernel/printk/printk.c

index 955e31860095ed01b511d52e43b1f9211c6dec69..5e038e78225650b6247e2a4a0785c0545954835a 100644 (file)
@@ -60,6 +60,9 @@ static inline const char *printk_skip_headers(const char *buffer)
 #define CONSOLE_LOGLEVEL_DEFAULT CONFIG_CONSOLE_LOGLEVEL_DEFAULT
 #define CONSOLE_LOGLEVEL_QUIET  CONFIG_CONSOLE_LOGLEVEL_QUIET
 
+int add_preferred_console_match(const char *match, const char *name,
+                               const short idx);
+
 extern int console_printk[];
 
 #define console_loglevel (console_printk[0])
index 39a2b61c7232e7b65d64eafd3ceadcf72cd32760..040fe7d1eda2f4f7533275f900c141ef95adc80f 100644 (file)
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-y  = printk.o
+obj-y  = printk.o conopt.o
 obj-$(CONFIG_PRINTK)   += printk_safe.o nbcon.o
 obj-$(CONFIG_A11Y_BRAILLE_CONSOLE)     += braille.o
 obj-$(CONFIG_PRINTK_INDEX)     += index.o
diff --git a/kernel/printk/conopt.c b/kernel/printk/conopt.c
new file mode 100644 (file)
index 0000000..9d507ba
--- /dev/null
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Kernel command line console options for hardware based addressing
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Tony Lindgren <tony@atomide.com>
+ */
+
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <asm/errno.h>
+
+#include "console_cmdline.h"
+
+/*
+ * Allow longer DEVNAME:0.0 style console naming such as abcd0000.serial:0.0
+ * in addition to the legacy ttyS0 style naming.
+ */
+#define CONSOLE_NAME_MAX       32
+
+#define CONSOLE_OPT_MAX                16
+#define CONSOLE_BRL_OPT_MAX    16
+
+struct console_option {
+       char name[CONSOLE_NAME_MAX];
+       char opt[CONSOLE_OPT_MAX];
+       char brl_opt[CONSOLE_BRL_OPT_MAX];
+       u8 has_brl_opt:1;
+};
+
+/* Updated only at console_setup() time, no locking needed */
+static struct console_option conopt[MAX_CMDLINECONSOLES];
+
+/**
+ * console_opt_save - Saves kernel command line console option for driver use
+ * @str: Kernel command line console name and option
+ * @brl_opt: Braille console options
+ *
+ * Saves a kernel command line console option for driver subsystems to use for
+ * adding a preferred console during init. Called from console_setup() only.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int __init console_opt_save(const char *str, const char *brl_opt)
+{
+       struct console_option *con;
+       size_t namelen, optlen;
+       const char *opt;
+       int i;
+
+       namelen = strcspn(str, ",");
+       if (namelen == 0 || namelen >= CONSOLE_NAME_MAX)
+               return -EINVAL;
+
+       opt = str + namelen;
+       if (*opt == ',')
+               opt++;
+
+       optlen = strlen(opt);
+       if (optlen >= CONSOLE_OPT_MAX)
+               return -EINVAL;
+
+       for (i = 0; i < MAX_CMDLINECONSOLES; i++) {
+               con = &conopt[i];
+
+               if (con->name[0]) {
+                       if (!strncmp(str, con->name, namelen))
+                               return 0;
+                       continue;
+               }
+
+               /*
+                * The name isn't terminated, only opt is. Empty opt is fine,
+                * but brl_opt can be either empty or NULL. For more info, see
+                * _braille_console_setup().
+                */
+               strscpy(con->name, str, namelen + 1);
+               strscpy(con->opt, opt, CONSOLE_OPT_MAX);
+               if (brl_opt) {
+                       strscpy(con->brl_opt, brl_opt, CONSOLE_BRL_OPT_MAX);
+                       con->has_brl_opt = 1;
+               }
+
+               return 0;
+       }
+
+       return -ENOMEM;
+}
+
+static struct console_option *console_opt_find(const char *name)
+{
+       struct console_option *con;
+       int i;
+
+       for (i = 0; i < MAX_CMDLINECONSOLES; i++) {
+               con = &conopt[i];
+               if (!strcmp(name, con->name))
+                       return con;
+       }
+
+       return NULL;
+}
+
+/**
+ * add_preferred_console_match - Adds a preferred console if a match is found
+ * @match: Expected console on kernel command line, such as console=DEVNAME:0.0
+ * @name: Name of the console character device to add such as ttyS
+ * @idx: Index for the console
+ *
+ * Allows driver subsystems to add a console after translating the command
+ * line name to the character device name used for the console. Options are
+ * added automatically based on the kernel command line. Duplicate preferred
+ * consoles are ignored by __add_preferred_console().
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int add_preferred_console_match(const char *match, const char *name,
+                               const short idx)
+{
+       struct console_option *con;
+       char *brl_opt = NULL;
+
+       if (!match || !strlen(match) || !name || !strlen(name) ||
+           idx < 0)
+               return -EINVAL;
+
+       con = console_opt_find(match);
+       if (!con)
+               return -ENOENT;
+
+       /*
+        * See __add_preferred_console(). It checks for NULL brl_options to set
+        * the preferred_console flag. Empty brl_opt instead of NULL leads into
+        * the preferred_console flag not set, and CON_CONSDEV not being set,
+        * and the boot console won't get disabled at the end of console_setup().
+        */
+       if (con->has_brl_opt)
+               brl_opt = con->brl_opt;
+
+       console_opt_add_preferred_console(name, idx, con->opt, brl_opt);
+
+       return 0;
+}
index 3ca74ad391d6e88daf0d63f3085a7991daad361f..a125e0235589d56b549e0d8903fb52c110c9d4dc 100644 (file)
@@ -2,6 +2,12 @@
 #ifndef _CONSOLE_CMDLINE_H
 #define _CONSOLE_CMDLINE_H
 
+#define MAX_CMDLINECONSOLES 8
+
+int console_opt_save(const char *str, const char *brl_opt);
+int console_opt_add_preferred_console(const char *name, const short idx,
+                                     char *options, char *brl_options);
+
 struct console_cmdline
 {
        char    name[16];                       /* Name of the driver       */
index adf99c05adcafc494a003294e0de525005271df6..05919491c5a20b5d3fa3dd3796d500eced89421e 100644 (file)
@@ -383,9 +383,6 @@ static int console_locked;
 /*
  *     Array of consoles built from command line options (console=)
  */
-
-#define MAX_CMDLINECONSOLES 8
-
 static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
 
 static int preferred_console = -1;
@@ -2503,6 +2500,10 @@ static int __init console_setup(char *str)
        if (_braille_console_setup(&str, &brl_options))
                return 1;
 
+       /* Save the console for driver subsystem use */
+       if (console_opt_save(str, brl_options))
+               return 1;
+
        /*
         * Decode str into name, index, options.
         */
@@ -2533,6 +2534,13 @@ static int __init console_setup(char *str)
 }
 __setup("console=", console_setup);
 
+/* Only called from add_preferred_console_match() */
+int console_opt_add_preferred_console(const char *name, const short idx,
+                                     char *options, char *brl_options)
+{
+       return __add_preferred_console(name, idx, options, brl_options, true);
+}
+
 /**
  * add_preferred_console - add a device to the list of preferred consoles.
  * @name: device name