[PATCH 2/2] x86/earlyprintk: setup earlyprintk as early as possible

Alexander Kuleshov
2015-04-08T09:32:11Z

As setup_early_printk passed to the early_param, it will be usable only after
'parse_early_param' function will be called from the 'setup_arch'. So we have
earlyprintk during early boot and decompression. Next point after decompression
of the kernel where we can use early_printk is after call of the
'parse_early_param'.

This patch makes setup_early_printk visible for head{32,64}.c So 'early_printk'
function will be usabable after decompression of the kernel and before
parse_early_param will be called. It also must be safe if CONFIG_CMDLINE_BOOL
and CONFIG_CMDLINE_OVERRIDE are set, because setup_cmdline function will be
called before setup_early_printk.

Tested it with qemu, so early_printk() is usable and prints to serial console
right after setup_early_printk function called from arch/x86/kerne/head64.c

Changes v1->v2:

* Comment added before the setup_early_printk call;
* Added information about testing to the commit message.

Changes v2->v3:

* Call setup_cmdline before setup_early_printk;
* setup_early_printk call wrapped with the setup_early_serial_console which
checks that 'serial' given to the earlyprintk command line option. This
prevents call of the setup_early_printk with the given pciserial/dbgp/efi,
because they are using early_ioremap.

Signed-off-by: Alexander Kuleshov <kuleshovmail@gmail.com>
---
arch/x86/kernel/early_printk.c | 2 +-
arch/x86/kernel/head.c | 14 ++++++++++++++
arch/x86/kernel/head32.c | 2 ++
arch/x86/kernel/head64.c | 7 ++++++-
include/linux/printk.h | 4 ++++
kernel/printk/printk.c | 11 +++++++----
6 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c
index a62536a..19a94e0 100644
--- a/arch/x86/kernel/early_printk.c
+++ b/arch/x86/kernel/early_printk.c
@@ -329,7 +329,7 @@ static inline void early_console_register(struct console *con, int keep_early)
register_console(early_console);
}

-static int __init setup_early_printk(char *buf)
+int __init setup_early_printk(char *buf)
{
int keep;

diff --git a/arch/x86/kernel/head.c b/arch/x86/kernel/head.c
index 992f442..b6e72aa 100644
--- a/arch/x86/kernel/head.c
+++ b/arch/x86/kernel/head.c
@@ -69,3 +69,17 @@ void __init reserve_ebda_region(void)
/* reserve all memory between lowmem and the 1MB mark */
memblock_reserve(lowmem, 0x100000 - lowmem);
}
+
+void setup_early_serial_console() {
+#ifdef CONFIG_EARLY_PRINTK
+ char *arg;
+
+ arg = strstr(boot_command_line, "earlyprintk=serial");
+ if (!arg)
+ arg = strstr(boot_command_line, "earlyprintk=ttyS");
+ if (!arg)
+ return;
+
+ setup_early_printk(boot_command_line);
+#endif
+}
diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c
index 7ad0ad0..64c7a80 100644
--- a/arch/x86/kernel/head32.c
+++ b/arch/x86/kernel/head32.c
@@ -32,6 +32,8 @@ static void __init i386_default_early_setup(void)
asmlinkage __visible void __init i386_start_kernel(void)
{
setup_cmdline();
+ /* setup serial console as early as possible */
+ setup_early_serial_console();
cr4_init_shadow();
sanitize_boot_params(&boot_params);

diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index 6eea2de..f0c03bd 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -172,12 +172,15 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)

copy_bootdata(__va(real_mode_data));
setup_cmdline();
+ /* setup serial console as early as possible */
+ setup_early_serial_console();

/*
* Load microcode early on BSP.
*/
load_ucode_bsp();

+
if (console_loglevel >= CONSOLE_LOGLEVEL_DEBUG)
early_printk("Kernel alive\n");

@@ -193,8 +196,10 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
void __init x86_64_start_reservations(char *real_mode_data)
{
/* version is always not zero if it is copied */
- if (!boot_params.hdr.version)
+ if (!boot_params.hdr.version) {
copy_bootdata(__va(real_mode_data));
+ setup_early_serial_console();
+ }

reserve_ebda_region();

diff --git a/include/linux/printk.h b/include/linux/printk.h
index baa3f97..17358dd 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -115,9 +115,13 @@ int no_printk(const char *fmt, ...)
#ifdef CONFIG_EARLY_PRINTK
extern asmlinkage __printf(1, 2)
void early_printk(const char *fmt, ...);
+int setup_early_printk(char *buf);
+void setup_early_serial_console(void);
#else
static inline __printf(1, 2) __cold
void early_printk(const char *s, ...) { }
+int setup_early_printk(char *buf) { }
+static void setup_early_serial_console(void) { }
#endif

typedef int(*printk_func_t)(const char *fmt, va_list args);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index bb0635b..e56285c 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2409,11 +2409,14 @@ void register_console(struct console *newcon)
struct console_cmdline *c;

if (console_drivers)
- for_each_console(bcon)
- if (WARN(bcon == newcon,
- "console '%s%d' already registered\n",
- bcon->name, bcon->index))
+ for_each_console(bcon) {
+ /* not again */
+ if (bcon == newcon) {
+ printk(KERN_INFO "console '%s%d' already registered\n",
+ bcon->name, bcon->index);
return;
+ }
+ }

/*
* before we register a new CON_BOOT console, make sure we don't
--
2.3.3.611.g09038fc.dirty