x86/mm: Introduce the 'no5lvl' kernel parameter
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>
Fri, 18 May 2018 10:35:25 +0000 (13:35 +0300)
committerIngo Molnar <mingo@kernel.org>
Sat, 19 May 2018 09:56:57 +0000 (11:56 +0200)
This kernel parameter allows to force kernel to use 4-level paging even
if hardware and kernel support 5-level paging.

The option may be useful to work around regressions related to 5-level
paging.

Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Hugh Dickins <hughd@google.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20180518103528.59260-5-kirill.shutemov@linux.intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Documentation/admin-guide/kernel-parameters.txt
arch/x86/boot/compressed/cmdline.c
arch/x86/boot/compressed/head_64.S
arch/x86/boot/compressed/pgtable_64.c
arch/x86/kernel/cpu/common.c
arch/x86/kernel/head64.c

index 11fc28e..364a33c 100644 (file)
                        emulation library even if a 387 maths coprocessor
                        is present.
 
+       no5lvl          [X86-64] Disable 5-level paging mode. Forces
+                       kernel to use 4-level paging instead.
+
        no_console_suspend
                        [HW] Never suspend the console
                        Disable suspending of consoles during suspend and
index 0cb3257..af6cda0 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include "misc.h"
 
-#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE
+#if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE || CONFIG_X86_5LEVEL
 
 static unsigned long fs;
 static inline void set_fs(unsigned long seg)
index 8169e8b..6403789 100644 (file)
@@ -365,6 +365,7 @@ ENTRY(startup_64)
         * this function call.
         */
        pushq   %rsi
+       movq    %rsi, %rdi              /* real mode address */
        call    paging_prepare
        popq    %rsi
 
index 23707e1..8c51075 100644 (file)
@@ -31,16 +31,23 @@ static char trampoline_save[TRAMPOLINE_32BIT_SIZE];
  */
 unsigned long *trampoline_32bit __section(.data);
 
-struct paging_config paging_prepare(void)
+extern struct boot_params *boot_params;
+int cmdline_find_option_bool(const char *option);
+
+struct paging_config paging_prepare(void *rmode)
 {
        struct paging_config paging_config = {};
        unsigned long bios_start, ebda_start;
 
+       /* Initialize boot_params. Required for cmdline_find_option_bool(). */
+       boot_params = rmode;
+
        /*
         * Check if LA57 is desired and supported.
         *
-        * There are two parts to the check:
+        * There are several parts to the check:
         *   - if the kernel supports 5-level paging: CONFIG_X86_5LEVEL=y
+        *   - if user asked to disable 5-level paging: no5lvl in cmdline
         *   - if the machine supports 5-level paging:
         *     + CPUID leaf 7 is supported
         *     + the leaf has the feature bit set
@@ -48,6 +55,7 @@ struct paging_config paging_prepare(void)
         * That's substitute for boot_cpu_has() in early boot code.
         */
        if (IS_ENABLED(CONFIG_X86_5LEVEL) &&
+                       !cmdline_find_option_bool("no5lvl") &&
                        native_cpuid_eax(0) >= 7 &&
                        (native_cpuid_ecx(7) & (1 << (X86_FEATURE_LA57 & 31)))) {
                paging_config.l5_required = 1;
index 39ed2e6..27f68d1 100644 (file)
@@ -1028,6 +1028,21 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
         */
        setup_clear_cpu_cap(X86_FEATURE_PCID);
 #endif
+
+       /*
+        * Later in the boot process pgtable_l5_enabled() relies on
+        * cpu_feature_enabled(X86_FEATURE_LA57). If 5-level paging is not
+        * enabled by this point we need to clear the feature bit to avoid
+        * false-positives at the later stage.
+        *
+        * pgtable_l5_enabled() can be false here for several reasons:
+        *  - 5-level paging is disabled compile-time;
+        *  - it's 32-bit kernel;
+        *  - machine doesn't support 5-level paging;
+        *  - user specified 'no5lvl' in kernel command line.
+        */
+       if (!pgtable_l5_enabled())
+               setup_clear_cpu_cap(X86_FEATURE_LA57);
 }
 
 void __init early_cpu_init(void)
index 8d372d1..8047379 100644 (file)
@@ -80,10 +80,11 @@ static unsigned int __head *fixup_int(void *ptr, unsigned long physaddr)
 
 static bool __head check_la57_support(unsigned long physaddr)
 {
-       if (native_cpuid_eax(0) < 7)
-               return false;
-
-       if (!(native_cpuid_ecx(7) & (1 << (X86_FEATURE_LA57 & 31))))
+       /*
+        * 5-level paging is detected and enabled at kernel decomression
+        * stage. Only check if it has been enabled there.
+        */
+       if (!(native_read_cr4() & X86_CR4_LA57))
                return false;
 
        *fixup_int(&__pgtable_l5_enabled, physaddr) = 1;