printk: Save console options for add_preferred_console_match()

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>
This commit is contained in:
Tony Lindgren 2024-03-27 12:59:35 +02:00 committed by Greg Kroah-Hartman
parent e51c3e1d23
commit f03e8c1060
5 changed files with 167 additions and 4 deletions

View 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])

View 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

146
kernel/printk/conopt.c Normal file
View File

@ -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;
}

View 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 */

View 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