]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
x86/alternatives, kvm: Fix a couple of CALLs without a frame pointer
authorBorislav Petkov (AMD) <bp@alien8.de>
Tue, 18 Jun 2024 19:57:27 +0000 (21:57 +0200)
committerBorislav Petkov (AMD) <bp@alien8.de>
Mon, 1 Jul 2024 10:41:11 +0000 (12:41 +0200)
objtool complains:

  arch/x86/kvm/kvm.o: warning: objtool: .altinstr_replacement+0xc5: call without frame pointer save/setup
  vmlinux.o: warning: objtool: .altinstr_replacement+0x2eb: call without frame pointer save/setup

Make sure %rSP is an output operand to the respective asm() statements.

The test_cc() hunk and ALT_OUTPUT_SP() courtesy of peterz. Also from him
add some helpful debugging info to the documentation.

Now on to the explanations:

tl;dr: The alternatives macros are pretty fragile.

If I do ALT_OUTPUT_SP(output) in order to be able to package in a %rsp
reference for objtool so that a stack frame gets properly generated, the
inline asm input operand with positional argument 0 in clear_page():

"0" (page)

gets "renumbered" due to the added

: "+r" (current_stack_pointer), "=D" (page)

and then gcc says:

  ./arch/x86/include/asm/page_64.h:53:9: error: inconsistent operand constraints in an ‘asm’

The fix is to use an explicit "D" constraint which points to a singleton
register class (gcc terminology) which ends up doing what is expected
here: the page pointer - input and output - should be in the same %rdi
register.

Other register classes have more than one register in them - example:
"r" and "=r" or "A":

  ‘A’
The ‘a’ and ‘d’ registers.  This class is used for
instructions that return double word results in the ‘ax:dx’
register pair.  Single word values will be allocated either in
‘ax’ or ‘dx’.

so using "D" and "=D" just works in this particular case.

And yes, one would say, sure, why don't you do "+D" but then:

  : "+r" (current_stack_pointer), "+D" (page)
  : [old] "i" (clear_page_orig), [new1] "i" (clear_page_rep), [new2] "i" (clear_page_erms),
  : "cc", "memory", "rax", "rcx")

now find the Waldo^Wcomma which throws a wrench into all this.

Because that silly macro has an "input..." consume-all last macro arg
and in it, one is supposed to supply input *and* clobbers, leading to
silly syntax snafus.

Yap, they need to be cleaned up, one fine day...

Closes: https://lore.kernel.org/oe-kbuild-all/202406141648.jO9qNGLa-lkp@intel.com/
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Sean Christopherson <seanjc@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/r/20240625112056.GDZnqoGDXgYuWBDUwu@fat_crate.local
arch/x86/include/asm/alternative.h
arch/x86/include/asm/page_64.h
arch/x86/kernel/alternative.c
arch/x86/kvm/emulate.c
tools/objtool/Documentation/objtool.txt

index 89fa50d27a08b135d6d619a7bc314366367f2ae6..ca9ae606aab9acaa988c98e9ce5a8bb46aec7ac0 100644 (file)
@@ -246,9 +246,10 @@ static inline int alternatives_text_reserved(void *start, void *end)
  * references: i.e., if used for a function, it would add the PLT
  * suffix.
  */
-#define alternative_call(oldfunc, newfunc, ft_flags, output, input...) \
-       asm_inline volatile(ALTERNATIVE("call %c[old]", "call %c[new]", ft_flags) \
-               : output : [old] "i" (oldfunc), [new] "i" (newfunc), ## input)
+#define alternative_call(oldfunc, newfunc, ft_flags, output, input...)                 \
+       asm_inline volatile(ALTERNATIVE("call %c[old]", "call %c[new]", ft_flags)       \
+               : ALT_OUTPUT_SP(output)                                                 \
+               : [old] "i" (oldfunc), [new] "i" (newfunc), ## input)
 
 /*
  * Like alternative_call, but there are two features and respective functions.
@@ -260,7 +261,7 @@ static inline int alternatives_text_reserved(void *start, void *end)
                           output, input...)                                            \
        asm_inline volatile(ALTERNATIVE_2("call %c[old]", "call %c[new1]", ft_flags1,   \
                "call %c[new2]", ft_flags2)                                             \
-               : output, ASM_CALL_CONSTRAINT                                           \
+               : ALT_OUTPUT_SP(output)                                                 \
                : [old] "i" (oldfunc), [new1] "i" (newfunc1),                           \
                  [new2] "i" (newfunc2), ## input)
 
@@ -276,6 +277,8 @@ static inline int alternatives_text_reserved(void *start, void *end)
  */
 #define ASM_NO_INPUT_CLOBBER(clbr...) "i" (0) : clbr
 
+#define ALT_OUTPUT_SP(...) ASM_CALL_CONSTRAINT, ## __VA_ARGS__
+
 /* Macro for creating assembler functions avoiding any C magic. */
 #define DEFINE_ASM_FUNC(func, instr, sec)              \
        asm (".pushsection " #sec ", \"ax\"\n"          \
index cc6b8e087192e4c3b6901e5aab6471b1629d9849..af4302d79b59bd05ea510f8c187c8192604fee47 100644 (file)
@@ -54,7 +54,7 @@ static inline void clear_page(void *page)
                           clear_page_rep, X86_FEATURE_REP_GOOD,
                           clear_page_erms, X86_FEATURE_ERMS,
                           "=D" (page),
-                          "0" (page)
+                          "D" (page)
                           : "cc", "memory", "rax", "rcx");
 }
 
index 37596a4170943798677c20d04ad50a8e9d01cddb..333b16181357da08529fbf775a35e639401dff62 100644 (file)
@@ -1657,7 +1657,7 @@ static noinline void __init alt_reloc_selftest(void)
         */
        asm_inline volatile (
                ALTERNATIVE("", "lea %[mem], %%" _ASM_ARG1 "; call __alt_reloc_selftest;", X86_FEATURE_ALWAYS)
-               : /* output */
+               : ASM_CALL_CONSTRAINT
                : [mem] "m" (__alt_reloc_selftest_addr)
                : _ASM_ARG1
        );
index 5d4c86133453d88dbb4f1b5f34d04bde7ec45a55..c8cc578646d0c2d4d6baf4c5863a7c7ce3ef3f7b 100644 (file)
@@ -1069,7 +1069,7 @@ static __always_inline u8 test_cc(unsigned int condition, unsigned long flags)
 
        flags = (flags & EFLAGS_MASK) | X86_EFLAGS_IF;
        asm("push %[flags]; popf; " CALL_NOSPEC
-           : "=a"(rc) : [thunk_target]"r"(fop), [flags]"r"(flags));
+           : "=a"(rc), ASM_CALL_CONSTRAINT : [thunk_target]"r"(fop), [flags]"r"(flags));
        return rc;
 }
 
index fe39c2a8ef0db12a57a9b7c042bb821d002e9189..7c3ee959b63c71deffc44a84f46d0994a8153103 100644 (file)
@@ -284,6 +284,25 @@ the objtool maintainers.
 
    Otherwise the stack frame may not get created before the call.
 
+   objtool can help with pinpointing the exact function where it happens:
+
+   $ OBJTOOL_ARGS="--verbose" make arch/x86/kvm/
+
+   arch/x86/kvm/kvm.o: warning: objtool: .altinstr_replacement+0xc5: call without frame pointer save/setup
+   arch/x86/kvm/kvm.o: warning: objtool:   em_loop.part.0+0x29: (alt)
+   arch/x86/kvm/kvm.o: warning: objtool:   em_loop.part.0+0x0: <=== (sym)
+    LD [M]  arch/x86/kvm/kvm-intel.o
+   0000 0000000000028220 <em_loop.part.0>:
+   0000    28220:  0f b6 47 61             movzbl 0x61(%rdi),%eax
+   0004    28224:  3c e2                   cmp    $0xe2,%al
+   0006    28226:  74 2c                   je     28254 <em_loop.part.0+0x34>
+   0008    28228:  48 8b 57 10             mov    0x10(%rdi),%rdx
+   000c    2822c:  83 f0 05                xor    $0x5,%eax
+   000f    2822f:  48 c1 e0 04             shl    $0x4,%rax
+   0013    28233:  25 f0 00 00 00          and    $0xf0,%eax
+   0018    28238:  81 e2 d5 08 00 00       and    $0x8d5,%edx
+   001e    2823e:  80 ce 02                or     $0x2,%dh
+   ...
 
 2. file.o: warning: objtool: .text+0x53: unreachable instruction