xref: /linux/arch/x86/include/asm/frame.h (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2997963edSJosh Poimboeuf #ifndef _ASM_X86_FRAME_H
3997963edSJosh Poimboeuf #define _ASM_X86_FRAME_H
4997963edSJosh Poimboeuf 
54625cd63SJan Beulich #include <asm/asm.h>
6bb898558SAl Viro 
7997963edSJosh Poimboeuf /*
8997963edSJosh Poimboeuf  * These are stack frame creation macros.  They should be used by every
9997963edSJosh Poimboeuf  * callable non-leaf asm function to make kernel stack traces more reliable.
10997963edSJosh Poimboeuf  */
11ec518655SJosh Poimboeuf 
12bb898558SAl Viro #ifdef CONFIG_FRAME_POINTER
13997963edSJosh Poimboeuf 
14ec518655SJosh Poimboeuf #ifdef __ASSEMBLY__
15ec518655SJosh Poimboeuf 
16997963edSJosh Poimboeuf .macro FRAME_BEGIN
17997963edSJosh Poimboeuf 	push %_ASM_BP
18997963edSJosh Poimboeuf 	_ASM_MOV %_ASM_SP, %_ASM_BP
19bb898558SAl Viro .endm
20997963edSJosh Poimboeuf 
21997963edSJosh Poimboeuf .macro FRAME_END
22997963edSJosh Poimboeuf 	pop %_ASM_BP
23bb898558SAl Viro .endm
24997963edSJosh Poimboeuf 
25a9b3c699SPeter Zijlstra #ifdef CONFIG_X86_64
26a9b3c699SPeter Zijlstra /*
27a9b3c699SPeter Zijlstra  * This is a sneaky trick to help the unwinder find pt_regs on the stack.  The
28a9b3c699SPeter Zijlstra  * frame pointer is replaced with an encoded pointer to pt_regs.  The encoding
29a9b3c699SPeter Zijlstra  * is just setting the LSB, which makes it an invalid stack address and is also
30a9b3c699SPeter Zijlstra  * a signal to the unwinder that it's a pt_regs pointer in disguise.
31a9b3c699SPeter Zijlstra  *
32a9b3c699SPeter Zijlstra  * NOTE: This macro must be used *after* PUSH_AND_CLEAR_REGS because it corrupts
33a9b3c699SPeter Zijlstra  * the original rbp.
34a9b3c699SPeter Zijlstra  */
35a9b3c699SPeter Zijlstra .macro ENCODE_FRAME_POINTER ptregs_offset=0
36a9b3c699SPeter Zijlstra 	leaq 1+\ptregs_offset(%rsp), %rbp
37a9b3c699SPeter Zijlstra .endm
38a9b3c699SPeter Zijlstra #else /* !CONFIG_X86_64 */
39a9b3c699SPeter Zijlstra /*
40a9b3c699SPeter Zijlstra  * This is a sneaky trick to help the unwinder find pt_regs on the stack.  The
41a9b3c699SPeter Zijlstra  * frame pointer is replaced with an encoded pointer to pt_regs.  The encoding
42a9b3c699SPeter Zijlstra  * is just clearing the MSB, which makes it an invalid stack address and is also
43a9b3c699SPeter Zijlstra  * a signal to the unwinder that it's a pt_regs pointer in disguise.
44a9b3c699SPeter Zijlstra  *
45a9b3c699SPeter Zijlstra  * NOTE: This macro must be used *after* SAVE_ALL because it corrupts the
46a9b3c699SPeter Zijlstra  * original ebp.
47a9b3c699SPeter Zijlstra  */
48a9b3c699SPeter Zijlstra .macro ENCODE_FRAME_POINTER
49a9b3c699SPeter Zijlstra 	mov %esp, %ebp
50a9b3c699SPeter Zijlstra 	andl $0x7fffffff, %ebp
51a9b3c699SPeter Zijlstra .endm
52a9b3c699SPeter Zijlstra #endif /* CONFIG_X86_64 */
53a9b3c699SPeter Zijlstra 
54ec518655SJosh Poimboeuf #else /* !__ASSEMBLY__ */
55ec518655SJosh Poimboeuf 
56ec518655SJosh Poimboeuf #define FRAME_BEGIN				\
57ec518655SJosh Poimboeuf 	"push %" _ASM_BP "\n"			\
58ec518655SJosh Poimboeuf 	_ASM_MOV "%" _ASM_SP ", %" _ASM_BP "\n"
59ec518655SJosh Poimboeuf 
60ec518655SJosh Poimboeuf #define FRAME_END "pop %" _ASM_BP "\n"
61ec518655SJosh Poimboeuf 
62a9b3c699SPeter Zijlstra #ifdef CONFIG_X86_64
63*6f9885a3SJosh Poimboeuf 
64a9b3c699SPeter Zijlstra #define ENCODE_FRAME_POINTER			\
65a9b3c699SPeter Zijlstra 	"lea 1(%rsp), %rbp\n\t"
66*6f9885a3SJosh Poimboeuf 
encode_frame_pointer(struct pt_regs * regs)67*6f9885a3SJosh Poimboeuf static inline unsigned long encode_frame_pointer(struct pt_regs *regs)
68*6f9885a3SJosh Poimboeuf {
69*6f9885a3SJosh Poimboeuf 	return (unsigned long)regs + 1;
70*6f9885a3SJosh Poimboeuf }
71*6f9885a3SJosh Poimboeuf 
72a9b3c699SPeter Zijlstra #else /* !CONFIG_X86_64 */
73*6f9885a3SJosh Poimboeuf 
74a9b3c699SPeter Zijlstra #define ENCODE_FRAME_POINTER			\
75a9b3c699SPeter Zijlstra 	"movl %esp, %ebp\n\t"			\
76a9b3c699SPeter Zijlstra 	"andl $0x7fffffff, %ebp\n\t"
77*6f9885a3SJosh Poimboeuf 
encode_frame_pointer(struct pt_regs * regs)78*6f9885a3SJosh Poimboeuf static inline unsigned long encode_frame_pointer(struct pt_regs *regs)
79*6f9885a3SJosh Poimboeuf {
80*6f9885a3SJosh Poimboeuf 	return (unsigned long)regs & 0x7fffffff;
81*6f9885a3SJosh Poimboeuf }
82*6f9885a3SJosh Poimboeuf 
83a9b3c699SPeter Zijlstra #endif /* CONFIG_X86_64 */
84a9b3c699SPeter Zijlstra 
85ec518655SJosh Poimboeuf #endif /* __ASSEMBLY__ */
86ec518655SJosh Poimboeuf 
87997963edSJosh Poimboeuf #define FRAME_OFFSET __ASM_SEL(4, 8)
88997963edSJosh Poimboeuf 
89997963edSJosh Poimboeuf #else /* !CONFIG_FRAME_POINTER */
90997963edSJosh Poimboeuf 
91a9b3c699SPeter Zijlstra #ifdef __ASSEMBLY__
92a9b3c699SPeter Zijlstra 
93a9b3c699SPeter Zijlstra .macro ENCODE_FRAME_POINTER ptregs_offset=0
94a9b3c699SPeter Zijlstra .endm
95a9b3c699SPeter Zijlstra 
96a9b3c699SPeter Zijlstra #else /* !__ASSEMBLY */
97a9b3c699SPeter Zijlstra 
98a9b3c699SPeter Zijlstra #define ENCODE_FRAME_POINTER
99a9b3c699SPeter Zijlstra 
encode_frame_pointer(struct pt_regs * regs)100*6f9885a3SJosh Poimboeuf static inline unsigned long encode_frame_pointer(struct pt_regs *regs)
101*6f9885a3SJosh Poimboeuf {
102*6f9885a3SJosh Poimboeuf 	return 0;
103*6f9885a3SJosh Poimboeuf }
104*6f9885a3SJosh Poimboeuf 
105a9b3c699SPeter Zijlstra #endif
106a9b3c699SPeter Zijlstra 
107997963edSJosh Poimboeuf #define FRAME_BEGIN
108997963edSJosh Poimboeuf #define FRAME_END
109997963edSJosh Poimboeuf #define FRAME_OFFSET 0
110997963edSJosh Poimboeuf 
111997963edSJosh Poimboeuf #endif /* CONFIG_FRAME_POINTER */
112bb898558SAl Viro 
113997963edSJosh Poimboeuf #endif /* _ASM_X86_FRAME_H */
114