Warning: This file is not a C or C++ file. It does not have highlighting.
1 | /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ |
---|---|
2 | /* |
3 | * PowerPC specific definitions for NOLIBC |
4 | * Copyright (C) 2023 Zhangjin Wu <falcon@tinylab.org> |
5 | */ |
6 | |
7 | #ifndef _NOLIBC_ARCH_POWERPC_H |
8 | #define _NOLIBC_ARCH_POWERPC_H |
9 | |
10 | #include "compiler.h" |
11 | #include "crt.h" |
12 | |
13 | /* Syscalls for PowerPC : |
14 | * - stack is 16-byte aligned |
15 | * - syscall number is passed in r0 |
16 | * - arguments are in r3, r4, r5, r6, r7, r8, r9 |
17 | * - the system call is performed by calling "sc" |
18 | * - syscall return comes in r3, and the summary overflow bit is checked |
19 | * to know if an error occurred, in which case errno is in r3. |
20 | * - the arguments are cast to long and assigned into the target |
21 | * registers which are then simply passed as registers to the asm code, |
22 | * so that we don't have to experience issues with register constraints. |
23 | */ |
24 | |
25 | #define _NOLIBC_SYSCALL_CLOBBERLIST \ |
26 | "memory", "cr0", "r12", "r11", "r10", "r9" |
27 | |
28 | #define my_syscall0(num) \ |
29 | ({ \ |
30 | register long _ret __asm__ ("r3"); \ |
31 | register long _num __asm__ ("r0") = (num); \ |
32 | \ |
33 | __asm__ volatile ( \ |
34 | " sc\n" \ |
35 | " bns+ 1f\n" \ |
36 | " neg %0, %0\n" \ |
37 | "1:\n" \ |
38 | : "=r"(_ret), "+r"(_num) \ |
39 | : \ |
40 | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5", "r4" \ |
41 | ); \ |
42 | _ret; \ |
43 | }) |
44 | |
45 | #define my_syscall1(num, arg1) \ |
46 | ({ \ |
47 | register long _ret __asm__ ("r3"); \ |
48 | register long _num __asm__ ("r0") = (num); \ |
49 | register long _arg1 __asm__ ("r3") = (long)(arg1); \ |
50 | \ |
51 | __asm__ volatile ( \ |
52 | " sc\n" \ |
53 | " bns+ 1f\n" \ |
54 | " neg %0, %0\n" \ |
55 | "1:\n" \ |
56 | : "=r"(_ret), "+r"(_num) \ |
57 | : "0"(_arg1) \ |
58 | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5", "r4" \ |
59 | ); \ |
60 | _ret; \ |
61 | }) |
62 | |
63 | |
64 | #define my_syscall2(num, arg1, arg2) \ |
65 | ({ \ |
66 | register long _ret __asm__ ("r3"); \ |
67 | register long _num __asm__ ("r0") = (num); \ |
68 | register long _arg1 __asm__ ("r3") = (long)(arg1); \ |
69 | register long _arg2 __asm__ ("r4") = (long)(arg2); \ |
70 | \ |
71 | __asm__ volatile ( \ |
72 | " sc\n" \ |
73 | " bns+ 1f\n" \ |
74 | " neg %0, %0\n" \ |
75 | "1:\n" \ |
76 | : "=r"(_ret), "+r"(_num), "+r"(_arg2) \ |
77 | : "0"(_arg1) \ |
78 | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5" \ |
79 | ); \ |
80 | _ret; \ |
81 | }) |
82 | |
83 | |
84 | #define my_syscall3(num, arg1, arg2, arg3) \ |
85 | ({ \ |
86 | register long _ret __asm__ ("r3"); \ |
87 | register long _num __asm__ ("r0") = (num); \ |
88 | register long _arg1 __asm__ ("r3") = (long)(arg1); \ |
89 | register long _arg2 __asm__ ("r4") = (long)(arg2); \ |
90 | register long _arg3 __asm__ ("r5") = (long)(arg3); \ |
91 | \ |
92 | __asm__ volatile ( \ |
93 | " sc\n" \ |
94 | " bns+ 1f\n" \ |
95 | " neg %0, %0\n" \ |
96 | "1:\n" \ |
97 | : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3) \ |
98 | : "0"(_arg1) \ |
99 | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6" \ |
100 | ); \ |
101 | _ret; \ |
102 | }) |
103 | |
104 | |
105 | #define my_syscall4(num, arg1, arg2, arg3, arg4) \ |
106 | ({ \ |
107 | register long _ret __asm__ ("r3"); \ |
108 | register long _num __asm__ ("r0") = (num); \ |
109 | register long _arg1 __asm__ ("r3") = (long)(arg1); \ |
110 | register long _arg2 __asm__ ("r4") = (long)(arg2); \ |
111 | register long _arg3 __asm__ ("r5") = (long)(arg3); \ |
112 | register long _arg4 __asm__ ("r6") = (long)(arg4); \ |
113 | \ |
114 | __asm__ volatile ( \ |
115 | " sc\n" \ |
116 | " bns+ 1f\n" \ |
117 | " neg %0, %0\n" \ |
118 | "1:\n" \ |
119 | : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3), \ |
120 | "+r"(_arg4) \ |
121 | : "0"(_arg1) \ |
122 | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7" \ |
123 | ); \ |
124 | _ret; \ |
125 | }) |
126 | |
127 | |
128 | #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ |
129 | ({ \ |
130 | register long _ret __asm__ ("r3"); \ |
131 | register long _num __asm__ ("r0") = (num); \ |
132 | register long _arg1 __asm__ ("r3") = (long)(arg1); \ |
133 | register long _arg2 __asm__ ("r4") = (long)(arg2); \ |
134 | register long _arg3 __asm__ ("r5") = (long)(arg3); \ |
135 | register long _arg4 __asm__ ("r6") = (long)(arg4); \ |
136 | register long _arg5 __asm__ ("r7") = (long)(arg5); \ |
137 | \ |
138 | __asm__ volatile ( \ |
139 | " sc\n" \ |
140 | " bns+ 1f\n" \ |
141 | " neg %0, %0\n" \ |
142 | "1:\n" \ |
143 | : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3), \ |
144 | "+r"(_arg4), "+r"(_arg5) \ |
145 | : "0"(_arg1) \ |
146 | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8" \ |
147 | ); \ |
148 | _ret; \ |
149 | }) |
150 | |
151 | #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ |
152 | ({ \ |
153 | register long _ret __asm__ ("r3"); \ |
154 | register long _num __asm__ ("r0") = (num); \ |
155 | register long _arg1 __asm__ ("r3") = (long)(arg1); \ |
156 | register long _arg2 __asm__ ("r4") = (long)(arg2); \ |
157 | register long _arg3 __asm__ ("r5") = (long)(arg3); \ |
158 | register long _arg4 __asm__ ("r6") = (long)(arg4); \ |
159 | register long _arg5 __asm__ ("r7") = (long)(arg5); \ |
160 | register long _arg6 __asm__ ("r8") = (long)(arg6); \ |
161 | \ |
162 | __asm__ volatile ( \ |
163 | " sc\n" \ |
164 | " bns+ 1f\n" \ |
165 | " neg %0, %0\n" \ |
166 | "1:\n" \ |
167 | : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3), \ |
168 | "+r"(_arg4), "+r"(_arg5), "+r"(_arg6) \ |
169 | : "0"(_arg1) \ |
170 | : _NOLIBC_SYSCALL_CLOBBERLIST \ |
171 | ); \ |
172 | _ret; \ |
173 | }) |
174 | |
175 | #ifndef __powerpc64__ |
176 | /* FIXME: For 32-bit PowerPC, with newer gcc compilers (e.g. gcc 13.1.0), |
177 | * "omit-frame-pointer" fails with __attribute__((no_stack_protector)) but |
178 | * works with __attribute__((__optimize__("-fno-stack-protector"))) |
179 | */ |
180 | #ifdef __no_stack_protector |
181 | #undef __no_stack_protector |
182 | #define __no_stack_protector __attribute__((__optimize__("-fno-stack-protector"))) |
183 | #endif |
184 | #endif /* !__powerpc64__ */ |
185 | |
186 | /* startup code */ |
187 | void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void) |
188 | { |
189 | #ifdef __powerpc64__ |
190 | #if _CALL_ELF == 2 |
191 | /* with -mabi=elfv2, save TOC/GOT pointer to r2 |
192 | * r12 is global entry pointer, we use it to compute TOC from r12 |
193 | * https://www.llvm.org/devmtg/2014-04/PDFs/Talks/Euro-LLVM-2014-Weigand.pdf |
194 | * https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.pdf |
195 | */ |
196 | __asm__ volatile ( |
197 | "addis 2, 12, .TOC. - _start@ha\n" |
198 | "addi 2, 2, .TOC. - _start@l\n" |
199 | ); |
200 | #endif /* _CALL_ELF == 2 */ |
201 | |
202 | __asm__ volatile ( |
203 | "mr 3, 1\n" /* save stack pointer to r3, as arg1 of _start_c */ |
204 | "clrrdi 1, 1, 4\n" /* align the stack to 16 bytes */ |
205 | "li 0, 0\n" /* zero the frame pointer */ |
206 | "stdu 1, -32(1)\n" /* the initial stack frame */ |
207 | "bl _start_c\n" /* transfer to c runtime */ |
208 | ); |
209 | #else |
210 | __asm__ volatile ( |
211 | "mr 3, 1\n" /* save stack pointer to r3, as arg1 of _start_c */ |
212 | "clrrwi 1, 1, 4\n" /* align the stack to 16 bytes */ |
213 | "li 0, 0\n" /* zero the frame pointer */ |
214 | "stwu 1, -16(1)\n" /* the initial stack frame */ |
215 | "bl _start_c\n" /* transfer to c runtime */ |
216 | ); |
217 | #endif |
218 | __builtin_unreachable(); |
219 | } |
220 | |
221 | #endif /* _NOLIBC_ARCH_POWERPC_H */ |
222 |
Warning: This file is not a C or C++ file. It does not have highlighting.