2015-04-08 09:31:31

by Alexander Kuleshov

[permalink] [raw]
Subject: [PATCH 0/2] x86/earlyprintk: setup earlyprintk as early as possible

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 provides initialization of earlyprintk serial console as early
as possible in the arch/x86/kerne/head{32,64}.c

Alexander Kuleshov (2):
x86/setup: update boot_command_line with builtin_cmdline in separate
function
x86/earlyprintk: setup earlyprintk as early as possible

arch/x86/kernel/early_printk.c | 2 +-
arch/x86/kernel/head.c | 14 ++++++++++++++
arch/x86/kernel/head32.c | 3 +++
arch/x86/kernel/head64.c | 8 +++++++-
arch/x86/kernel/setup.c | 31 ++++++++++++++++++-------------
include/linux/init.h | 6 +++++-
include/linux/printk.h | 4 ++++
kernel/printk/printk.c | 11 +++++++----
8 files changed, 59 insertions(+), 20 deletions(-)

--
2.3.3.611.g09038fc.dirty


2015-04-08 09:31:58

by Alexander Kuleshov

[permalink] [raw]
Subject: [PATCH 1/2] x86/setup: update boot_command_line with builtin_cmdline in separate function

This patch introduces setup_cmdline function which appends/overrides
boot_command_linew with builtin_cmdline if CONFIG_CMDLINE_BOOL is set.
Previously this functional was in the setup_arch, but we need to move
it for getting actual command line as early as possible in the
arch/x86/kernel/head{32,64}.c

Signed-off-by: Alexander Kuleshov <[email protected]>
---
arch/x86/kernel/head32.c | 1 +
arch/x86/kernel/head64.c | 1 +
arch/x86/kernel/setup.c | 31 ++++++++++++++++++-------------
include/linux/init.h | 6 +++++-
4 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c
index 2911ef3..7ad0ad0 100644
--- a/arch/x86/kernel/head32.c
+++ b/arch/x86/kernel/head32.c
@@ -31,6 +31,7 @@ static void __init i386_default_early_setup(void)

asmlinkage __visible void __init i386_start_kernel(void)
{
+ setup_cmdline();
cr4_init_shadow();
sanitize_boot_params(&boot_params);

diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index c4f8d46..6eea2de 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -171,6 +171,7 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
load_idt((const struct desc_ptr *)&idt_descr);

copy_bootdata(__va(real_mode_data));
+ setup_cmdline();

/*
* Load microcode early on BSP.
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 0a2421c..d578f37 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -841,6 +841,24 @@ dump_kernel_offset(struct notifier_block *self, unsigned long v, void *p)
}

/*
+ * Append/Override boot command line with builtin command line if need
+ */
+void __init setup_cmdline(void) {
+#ifdef CONFIG_CMDLINE_BOOL
+#ifdef CONFIG_CMDLINE_OVERRIDE
+ strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
+#else
+ if (builtin_cmdline[0]) {
+ /* append boot loader cmdline to builtin */
+ strlcat(builtin_cmdline, " ", COMMAND_LINE_SIZE);
+ strlcat(builtin_cmdline, boot_command_line, COMMAND_LINE_SIZE);
+ strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
+ }
+#endif
+#endif
+}
+
+/*
* Determine if we were loaded by an EFI loader. If so, then we have also been
* passed the efi memmap, systab, etc., so we should use these data structures
* for initialization. Note, the efi init code path is determined by the
@@ -968,19 +986,6 @@ void __init setup_arch(char **cmdline_p)
bss_resource.start = __pa_symbol(__bss_start);
bss_resource.end = __pa_symbol(__bss_stop)-1;

-#ifdef CONFIG_CMDLINE_BOOL
-#ifdef CONFIG_CMDLINE_OVERRIDE
- strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
-#else
- if (builtin_cmdline[0]) {
- /* append boot loader cmdline to builtin */
- strlcat(builtin_cmdline, " ", COMMAND_LINE_SIZE);
- strlcat(builtin_cmdline, boot_command_line, COMMAND_LINE_SIZE);
- strlcpy(boot_command_line, builtin_cmdline, COMMAND_LINE_SIZE);
- }
-#endif
-#endif
-
strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;

diff --git a/include/linux/init.h b/include/linux/init.h
index 2df8e8d..af8f2ec 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -152,7 +152,11 @@ void setup_arch(char **);
void prepare_namespace(void);
void __init load_default_modules(void);
int __init init_rootfs(void);
-
+#ifdef CONFIG_CMDLINE_BOOL
+void __init setup_cmdline(void);
+#else
+static void __init setup_cmdline(void) {};
+#endif
extern void (*late_time_init)(void);

extern bool initcall_debug;
--
2.3.3.611.g09038fc.dirty

2015-04-08 09:32:11

by Alexander Kuleshov

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

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 <[email protected]>
---
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

2015-04-08 11:32:58

by Peter Hurley

[permalink] [raw]
Subject: Re: [PATCH 2/2] x86/earlyprintk: setup earlyprintk as early as possible

Hi Alexander,

On 04/08/2015 05:31 AM, Alexander Kuleshov wrote:
> 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 <[email protected]>
> ---
> 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() {

If you put this function in kernel/early_printk.c, then
setup_early_printk() can remain file scope.

> +#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);

Like Ingo already pointed out, you need to emit a printk() after the
call to setup_early_printk() which validates that earlier-than-early
is working.

Because this isn't. setup_early_printk() expects the argument
to point to the start of the earlyprintk= parameter; ie.,

earlyprintk=ttyS0,115200
^
buf


> +#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

These can't go here, because this will break the build of every non-x86
arch, since these symbols will not be defined in those arches. These need
to be in x86 includes only.

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

The WARN is still required here because the register_console() caller
is not always obvious.

A better way to achieve what you want is to not re-register the console
to begin with. Looking at early_console_register() and setup_early_printk(),
it seems like re-registration would already be prevented (since early_console
!= 0 after the first call to early_console_register())? Have you tried this?

Regards,
Peter Hurley

>
> /*
> * before we register a new CON_BOOT console, make sure we don't
>

2015-04-09 07:39:16

by Alexander Kuleshov

[permalink] [raw]
Subject: Re: [PATCH 2/2] x86/earlyprintk: setup earlyprintk as early as possible

Hello Peter,

2015-04-08 17:32 GMT+06:00 Peter Hurley <[email protected]>:
>> +
>> +void setup_early_serial_console() {
>
> If you put this function in kernel/early_printk.c, then
> setup_early_printk() can remain file scope.
>

Yes, right. will fix it.

>
> The WARN is still required here because the register_console() caller
> is not always obvious.
>
> A better way to achieve what you want is to not re-register the console
> to begin with. Looking at early_console_register() and setup_early_printk(),
> it seems like re-registration would already be prevented (since early_console
> != 0 after the first call to early_console_register())? Have you tried this?
>

hmm, now, thanks for the point will test it.

Thank you.