2 * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org>.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Neither the name of the author nor the names of its contributors
11 * may be used to endorse or promote products derived from this software
12 * without specific prior written permission.
14 * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <machine/asm.h>
28 __FBSDID("$FreeBSD$");
31 * The following notes ("cheat sheet") was provided by Peter Wemm.
36 * rdx (3rd arg, 2nd return)
41 * r10 (temp, static chain?)
63 * arg1 goes in %rdi, arg2 in %rsi, etc. return value is %rax (and
64 * secondary return, eg: pipe(2), in %rdx) %rcx,%rsi,%rdi etc are
65 * trashed by making a call to something. %rbx,%rbp,%r12-15 are the
66 * only registers preserved across a call. Note that unlike i386,
67 * %rsi and %rdi are scratch rather than preserved. FPU is
68 * different, args are in SSE registers rather than the x87 stack.
70 * Aside from the register calling conventions, amd64 can be treated
71 * very much like i386. Things like setjmp/longjmp etc were literal
72 * translations from i386 but with the register names updated, etc.
73 * The main gotcha is that FPU save/restore is in SSE format, which
74 * means a sparse 512 byte FPU context.
79 * Where do we define these?
81 #define MC_SIZE 800 /* sizeof mcontext_t */
82 #define MC_LEN_OFFSET (25*8) /* offset to mc_len from mcontext */
83 #define MC_FPFMT_OFFSET (26*8) /* offset to mc_fpformat from mcontext */
84 #define MC_FPFMT_NODEV 0x10000
85 #define MC_OWNEDFP_OFFSET (27*8) /* offset to mc_ownedfp from mcontext */
86 #define MC_OWNEDFP_NONE 0x20000
87 #define MC_OWNEDFP_FPU 0x20001
88 #define MC_OWNEDFP_PCB 0x20002
89 #define MC_FPREGS_OFFSET (28*8) /* offset to FP registers */
90 #define MC_FP_CW_OFFSET (28*8) /* offset to FP control word */
92 #define MC_RDI (1 * 8)
93 #define MC_RSI (2 * 8)
94 #define MC_RDX (3 * 8)
95 #define MC_RCX (4 * 8)
98 #define MC_RAX (7 * 8)
99 #define MC_RBX (8 * 8)
100 #define MC_RBP (9 * 8)
101 #define MC_R10 (10 * 8)
102 #define MC_R11 (11 * 8)
103 #define MC_R12 (12 * 8)
104 #define MC_R13 (13 * 8)
105 #define MC_R14 (14 * 8)
106 #define MC_R15 (15 * 8)
107 #define MC_FLAGS (18 * 8)
108 #define MC_RIP (20 * 8)
109 #define MC_CS (21 * 8)
110 #define MC_RFLAGS (22 * 8)
111 #define MC_RSP (23 * 8)
112 #define MC_SS (24 * 8)
114 #define REDZONE 128 /* size of the red zone */
117 * _amd64_ctx_save(mcontext_t *mcp)
119 * No values are saved to mc_trapno, mc_addr, mc_err and mc_cs.
120 * For the FPU state, only the floating point control word is stored.
122 ENTRY(_amd64_save_context)
123 cmpq $0, %rdi /* check for null pointer */
127 1: movq %rdi, MC_RDI(%rdi)
128 movq %rsi, MC_RSI(%rdi)
129 movq %rdx, MC_RDX(%rdi)
130 movq %rcx, MC_RCX(%rdi)
131 movq %r8, MC_R8(%rdi)
132 movq %r9, MC_R9(%rdi)
133 movq $1, MC_RAX(%rdi) /* return 1 when restored */
134 movq %rbx, MC_RBX(%rdi)
135 movq %rbp, MC_RBP(%rdi)
136 movq %r10, MC_R10(%rdi)
137 movq %r11, MC_R11(%rdi)
138 movq %r12, MC_R12(%rdi)
139 movq %r13, MC_R13(%rdi)
140 movq %r14, MC_R14(%rdi)
141 movq %r15, MC_R15(%rdi)
142 movq (%rsp), %rax /* get return address */
143 movq %rax, MC_RIP(%rdi) /* save return address (%rip) */
144 pushfq /* get flags */
146 movq %rax, MC_RFLAGS(%rdi) /* save flags */
147 movq %rsp, %rax /* setcontext pushes the return */
148 addq $8, %rax /* address onto the stack; */
149 movq %rax, MC_RSP(%rdi) /* account for this -- ???. */
150 movw %ss, MC_SS(%rdi)
151 fnstcw MC_FP_CW_OFFSET(%rdi) /* save FPU control word */
152 movq $MC_OWNEDFP_NONE, MC_OWNEDFP_OFFSET(%rdi) /* no FP */
153 movq $MC_FPFMT_NODEV, MC_FPFMT_OFFSET(%rdi)
154 movq $MC_SIZE, MC_LEN_OFFSET(%rdi)
155 xorq %rax, %rax /* return 0 */
159 * _amd64_ctx_restore(mcontext_t *mcp, intptr_t val, intptr_t *loc);
161 ENTRY(_amd64_restore_context)
162 cmpq $0, %rdi /* check for null pointer */
166 1: cmpq $MC_SIZE, MC_LEN_OFFSET(%rdi) /* is context valid? */
168 movq $-1, %rax /* bzzzt, invalid context */
170 2: movq MC_RCX(%rdi), %rcx
171 movq MC_R8(%rdi), %r8
172 movq MC_R9(%rdi), %r9
173 movq MC_RBX(%rdi), %rbx
174 movq MC_RBP(%rdi), %rbp
175 movq MC_R10(%rdi), %r10
176 movq MC_R11(%rdi), %r11
177 movq MC_R12(%rdi), %r12
178 movq MC_R13(%rdi), %r13
179 movq MC_R14(%rdi), %r14
180 movq MC_R15(%rdi), %r15
182 * if (mc_fpowned == MC_OWNEDFP_FPU || mc_fpowned == MC_OWNEDFP_PCB)
183 * restore XMM/SSE FP register format
185 cmpq $MC_OWNEDFP_NONE, MC_OWNEDFP_OFFSET(%rdi)
187 cmpq $MC_OWNEDFP_PCB, MC_OWNEDFP_OFFSET(%rdi)
189 cmpq $MC_OWNEDFP_FPU, MC_OWNEDFP_OFFSET(%rdi)
191 3: fxrstor MC_FPREGS_OFFSET(%rdi) /* restore XMM FP regs */
194 fldcw MC_FP_CW_OFFSET(%rdi)
195 5: movq MC_RSP(%rdi), %rsp /* switch to context stack */
197 movq MC_RIP(%rdi), %rax /* return address on stack */
199 movq MC_RDI(%rdi), %rax /* rdi on stack */
201 movq MC_RDX(%rdi), %rax /* rdx on stack */
203 movq MC_RSI(%rdi), %rax /* rsi on stack */
205 movq MC_RFLAGS(%rdi), %rax /* flags on stack*/
207 movq MC_RAX(%rdi), %rax /* restore rax */
208 /* At this point we're done with the context. */
209 cmpq $0, %rdx /* set *loc to val */
212 6: popfq /* restore flags */
213 popq %rsi /* restore rsi, rdx, and rdi */