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