1/* $OpenBSD: frameasm.h,v 1.27 2023/07/27 00:30:07 guenther Exp $ */
2/* $NetBSD: frameasm.h,v 1.1 2003/04/26 18:39:40 fvdl Exp $ */
3
4#ifndef _AMD64_MACHINE_FRAMEASM_H
5#define _AMD64_MACHINE_FRAMEASM_H
6
7/*
8 * Macros to define pushing/popping frames for interrupts, traps
9 * and system calls. Currently all the same; will diverge later.
10 */
11
12/*
13 * These are used on interrupt or trap entry or exit.
14 */
15#define INTR_SAVE_GPRS \
16 subq $120,%rsp ; \
17 INTR_SAVE_MOST_GPRS_NO_ADJ ; \
18 movq %rcx,TF_RCX(%rsp)
19#define INTR_SAVE_MOST_GPRS_NO_ADJ \
20 movq %r15,TF_R15(%rsp) ; \
21 movq %r14,TF_R14(%rsp) ; \
22 movq %r13,TF_R13(%rsp) ; \
23 movq %r12,TF_R12(%rsp) ; \
24 movq %r11,TF_R11(%rsp) ; \
25 movq %r10,TF_R10(%rsp) ; \
26 movq %r9,TF_R9(%rsp) ; \
27 movq %r8,TF_R8(%rsp) ; \
28 movq %rdi,TF_RDI(%rsp) ; \
29 movq %rsi,TF_RSI(%rsp) ; \
30 movq %rbp,TF_RBP(%rsp) ; \
31 leaq TF_RBP(%rsp),%rbp ; \
32 movq %rbx,TF_RBX(%rsp) ; \
33 movq %rdx,TF_RDX(%rsp) ; \
34 movq %rax,TF_RAX(%rsp)
35
36/*
37 * We clear registers when coming from userspace to prevent
38 * user-controlled values from being available for use in speculative
39 * execution in the kernel. %rsp and %rbp are the kernel values when
40 * this is used, so there are only 14 to clear. 32bit operations clear
41 * the register upper-halves automatically.
42 */
43#define INTR_CLEAR_GPRS \
44 xorl %eax,%eax ; \
45 xorl %ebx,%ebx ; \
46 xorl %ecx,%ecx ; \
47 xorl %edx,%edx ; \
48 xorl %esi,%esi ; \
49 xorl %edi,%edi ; \
50 xorl %r8d,%r8d ; \
51 xorl %r9d,%r9d ; \
52 xorl %r10d,%r10d ; \
53 xorl %r11d,%r11d ; \
54 xorl %r12d,%r12d ; \
55 xorl %r13d,%r13d ; \
56 xorl %r14d,%r14d ; \
57 xorl %r15d,%r15d
58
59
60/*
61 * For real interrupt code paths, where we can come from userspace.
62 * We only have an iretq_frame on entry.
63 */
64#define INTRENTRY_LABEL(label) X##label##_untramp
65#define INTRENTRY(label) \
66 endbr64 ; \
67 testb $SEL_RPL,IRETQ_CS(%rsp) ; \
68 je INTRENTRY_LABEL(label) ; \
69 swapgs ; \
70 FENCE_SWAPGS_MIS_TAKEN ; \
71 movq %rax,CPUVAR(SCRATCH) ; \
72 CODEPATCH_START ; \
73 movq CPUVAR(KERN_CR3),%rax ; \
74 movq %rax,%cr3 ; \
75 CODEPATCH_END(CPTAG_MELTDOWN_NOP);\
76 jmp 98f ; \
77END(X##label) ; \
78_ENTRY(INTRENTRY_LABEL(label)) /* from kernel */ \
79 FENCE_NO_SAFE_SMAP ; \
80 subq $TF_RIP,%rsp ; \
81 movq %rcx,TF_RCX(%rsp) ; \
82 jmp 99f ; \
83 _ALIGN_TRAPS ; \
8498: /* from userspace */ \
85 movq CPUVAR(KERN_RSP),%rax ; \
86 xchgq %rax,%rsp ; \
87 movq %rcx,TF_RCX(%rsp) ; \
88 RET_STACK_REFILL_WITH_RCX ; \
89 /* copy iretq frame to the trap frame */ \
90 movq IRETQ_RIP(%rax),%rcx ; \
91 movq %rcx,TF_RIP(%rsp) ; \
92 movq IRETQ_CS(%rax),%rcx ; \
93 movq %rcx,TF_CS(%rsp) ; \
94 movq IRETQ_RFLAGS(%rax),%rcx ; \
95 movq %rcx,TF_RFLAGS(%rsp) ; \
96 movq IRETQ_RSP(%rax),%rcx ; \
97 movq %rcx,TF_RSP(%rsp) ; \
98 movq IRETQ_SS(%rax),%rcx ; \
99 movq %rcx,TF_SS(%rsp) ; \
100 movq CPUVAR(SCRATCH),%rax ; \
10199: INTR_SAVE_MOST_GPRS_NO_ADJ ; \
102 INTR_CLEAR_GPRS ; \
103 movq %rax,TF_ERR(%rsp)
104
105#define INTRFASTEXIT \
106 jmp intr_fast_exit
107
108/*
109 * Entry for faking up an interrupt frame after spllower() unblocks
110 * a previously received interrupt. On entry, %r13 has the %rip
111 * to return to. %r10 and %r11 are scratch.
112 */
113#define INTR_RECURSE \
114 endbr64 ; \
115 /* fake the iretq_frame */ ; \
116 movq %rsp,%r10 ; \
117 movl %ss,%r11d ; \
118 pushq %r11 ; \
119 pushq %r10 ; \
120 pushfq ; \
121 movl %cs,%r11d ; \
122 pushq %r11 ; \
123 pushq %r13 ; \
124 /* now do the rest of the intrframe */ \
125 subq $16,%rsp ; \
126 INTR_SAVE_GPRS
127
128
129/*
130 * Entry for traps from kernel, where there's a trapno + err already
131 * on the stack. We have to move the err from its hardware location
132 * to the location we want it.
133 */
134#define TRAP_ENTRY_KERN \
135 subq $120,%rsp ; \
136 movq %rcx,TF_RCX(%rsp) ; \
137 movq (TF_RIP - 8)(%rsp),%rcx ; \
138 movq %rcx,TF_ERR(%rsp) ; \
139 INTR_SAVE_MOST_GPRS_NO_ADJ
140
141/*
142 * Entry for traps from userland, where there's a trapno + err on
143 * the iretq stack.
144 * Assumes that %rax has been saved in CPUVAR(SCRATCH).
145 */
146#define TRAP_ENTRY_USER \
147 movq CPUVAR(KERN_RSP),%rax ; \
148 xchgq %rax,%rsp ; \
149 movq %rcx,TF_RCX(%rsp) ; \
150 RET_STACK_REFILL_WITH_RCX ; \
151 /* copy trapno+err to the trap frame */ \
152 movq 0(%rax),%rcx ; \
153 movq %rcx,TF_TRAPNO(%rsp) ; \
154 movq 8(%rax),%rcx ; \
155 movq %rcx,TF_ERR(%rsp) ; \
156 /* copy iretq frame to the trap frame */ \
157 movq (IRETQ_RIP+16)(%rax),%rcx ; \
158 movq %rcx,TF_RIP(%rsp) ; \
159 movq (IRETQ_CS+16)(%rax),%rcx ; \
160 movq %rcx,TF_CS(%rsp) ; \
161 movq (IRETQ_RFLAGS+16)(%rax),%rcx ; \
162 movq %rcx,TF_RFLAGS(%rsp) ; \
163 movq (IRETQ_RSP+16)(%rax),%rcx ; \
164 movq %rcx,TF_RSP(%rsp) ; \
165 movq (IRETQ_SS+16)(%rax),%rcx ; \
166 movq %rcx,TF_SS(%rsp) ; \
167 movq CPUVAR(SCRATCH),%rax ; \
168 INTR_SAVE_MOST_GPRS_NO_ADJ ; \
169 INTR_CLEAR_GPRS
170
171/*
172 * Entry from syscall instruction, where RIP is in %rcx and RFLAGS is in %r11.
173 * We stash the syscall # in tf_err for SPL check.
174 * Assumes that %rax has been saved in CPUVAR(SCRATCH).
175 */
176#define SYSCALL_ENTRY \
177 movq CPUVAR(KERN_RSP),%rax ; \
178 xchgq %rax,%rsp ; \
179 movq %rcx,TF_RCX(%rsp) ; \
180 movq %rcx,TF_RIP(%rsp) ; \
181 RET_STACK_REFILL_WITH_RCX ; \
182 movq $(GSEL(GUDATA_SEL, SEL_UPL)),TF_SS(%rsp) ; \
183 movq %rax,TF_RSP(%rsp) ; \
184 movq CPUVAR(SCRATCH),%rax ; \
185 INTR_SAVE_MOST_GPRS_NO_ADJ ; \
186 movq %r11, TF_RFLAGS(%rsp) ; \
187 movq $(GSEL(GUCODE_SEL, SEL_UPL)), TF_CS(%rsp) ; \
188 movq %rax,TF_ERR(%rsp) ; \
189 INTR_CLEAR_GPRS
190
191#define CHECK_ASTPENDING(reg) movq CPUVAR(CURPROC),reg ; \
192 cmpq $0, reg ; \
193 je 99f ; \
194 cmpl $0, P_MD_ASTPENDING(reg) ; \
195 99:
196
197#define CLEAR_ASTPENDING(reg) movl $0, P_MD_ASTPENDING(reg)
198
199#endif /* _AMD64_MACHINE_FRAMEASM_H */