xref: /openbmc/linux/tools/include/nolibc/arch-powerpc.h (revision fac59652993f075d57860769c99045b3ca18780d)
10cb0675eSZhangjin Wu /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
20cb0675eSZhangjin Wu /*
30cb0675eSZhangjin Wu  * PowerPC specific definitions for NOLIBC
40cb0675eSZhangjin Wu  * Copyright (C) 2023 Zhangjin Wu <falcon@tinylab.org>
50cb0675eSZhangjin Wu  */
60cb0675eSZhangjin Wu 
70cb0675eSZhangjin Wu #ifndef _NOLIBC_ARCH_POWERPC_H
80cb0675eSZhangjin Wu #define _NOLIBC_ARCH_POWERPC_H
90cb0675eSZhangjin Wu 
100cb0675eSZhangjin Wu #include "compiler.h"
110cb0675eSZhangjin Wu #include "crt.h"
120cb0675eSZhangjin Wu 
130cb0675eSZhangjin Wu /* Syscalls for PowerPC :
140cb0675eSZhangjin Wu  *   - stack is 16-byte aligned
150cb0675eSZhangjin Wu  *   - syscall number is passed in r0
160cb0675eSZhangjin Wu  *   - arguments are in r3, r4, r5, r6, r7, r8, r9
170cb0675eSZhangjin Wu  *   - the system call is performed by calling "sc"
180cb0675eSZhangjin Wu  *   - syscall return comes in r3, and the summary overflow bit is checked
190cb0675eSZhangjin Wu  *     to know if an error occurred, in which case errno is in r3.
200cb0675eSZhangjin Wu  *   - the arguments are cast to long and assigned into the target
210cb0675eSZhangjin Wu  *     registers which are then simply passed as registers to the asm code,
220cb0675eSZhangjin Wu  *     so that we don't have to experience issues with register constraints.
230cb0675eSZhangjin Wu  */
240cb0675eSZhangjin Wu 
250cb0675eSZhangjin Wu #define _NOLIBC_SYSCALL_CLOBBERLIST \
260cb0675eSZhangjin Wu 	"memory", "cr0", "r12", "r11", "r10", "r9"
270cb0675eSZhangjin Wu 
280cb0675eSZhangjin Wu #define my_syscall0(num)                                                     \
290cb0675eSZhangjin Wu ({                                                                           \
300cb0675eSZhangjin Wu 	register long _ret  __asm__ ("r3");                                  \
310cb0675eSZhangjin Wu 	register long _num  __asm__ ("r0") = (num);                          \
320cb0675eSZhangjin Wu 									     \
330cb0675eSZhangjin Wu 	__asm__ volatile (                                                   \
340cb0675eSZhangjin Wu 		"	sc\n"                                                \
350cb0675eSZhangjin Wu 		"	bns+ 1f\n"                                           \
360cb0675eSZhangjin Wu 		"	neg  %0, %0\n"                                       \
370cb0675eSZhangjin Wu 		"1:\n"                                                       \
380cb0675eSZhangjin Wu 		: "=r"(_ret), "+r"(_num)                                     \
390cb0675eSZhangjin Wu 		:                                                            \
400cb0675eSZhangjin Wu 		: _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5", "r4"  \
410cb0675eSZhangjin Wu 	);                                                                   \
420cb0675eSZhangjin Wu 	_ret;                                                                \
430cb0675eSZhangjin Wu })
440cb0675eSZhangjin Wu 
450cb0675eSZhangjin Wu #define my_syscall1(num, arg1)                                               \
460cb0675eSZhangjin Wu ({                                                                           \
470cb0675eSZhangjin Wu 	register long _ret  __asm__ ("r3");                                  \
480cb0675eSZhangjin Wu 	register long _num  __asm__ ("r0") = (num);                          \
490cb0675eSZhangjin Wu 	register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
500cb0675eSZhangjin Wu 									     \
510cb0675eSZhangjin Wu 	__asm__ volatile (                                                   \
520cb0675eSZhangjin Wu 		"	sc\n"                                                \
530cb0675eSZhangjin Wu 		"	bns+ 1f\n"                                           \
540cb0675eSZhangjin Wu 		"	neg  %0, %0\n"                                       \
550cb0675eSZhangjin Wu 		"1:\n"                                                       \
560cb0675eSZhangjin Wu 		: "=r"(_ret), "+r"(_num)                                     \
570cb0675eSZhangjin Wu 		: "0"(_arg1)                                                 \
580cb0675eSZhangjin Wu 		: _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5", "r4"  \
590cb0675eSZhangjin Wu 	);                                                                   \
600cb0675eSZhangjin Wu 	_ret;                                                                \
610cb0675eSZhangjin Wu })
620cb0675eSZhangjin Wu 
630cb0675eSZhangjin Wu 
640cb0675eSZhangjin Wu #define my_syscall2(num, arg1, arg2)                                         \
650cb0675eSZhangjin Wu ({                                                                           \
660cb0675eSZhangjin Wu 	register long _ret  __asm__ ("r3");                                  \
670cb0675eSZhangjin Wu 	register long _num  __asm__ ("r0") = (num);                          \
680cb0675eSZhangjin Wu 	register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
690cb0675eSZhangjin Wu 	register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
700cb0675eSZhangjin Wu 									     \
710cb0675eSZhangjin Wu 	__asm__ volatile (                                                   \
720cb0675eSZhangjin Wu 		"	sc\n"                                                \
730cb0675eSZhangjin Wu 		"	bns+ 1f\n"                                           \
740cb0675eSZhangjin Wu 		"	neg  %0, %0\n"                                       \
750cb0675eSZhangjin Wu 		"1:\n"                                                       \
760cb0675eSZhangjin Wu 		: "=r"(_ret), "+r"(_num), "+r"(_arg2)                        \
770cb0675eSZhangjin Wu 		: "0"(_arg1)                                                 \
780cb0675eSZhangjin Wu 		: _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5"        \
790cb0675eSZhangjin Wu 	);                                                                   \
800cb0675eSZhangjin Wu 	_ret;                                                                \
810cb0675eSZhangjin Wu })
820cb0675eSZhangjin Wu 
830cb0675eSZhangjin Wu 
840cb0675eSZhangjin Wu #define my_syscall3(num, arg1, arg2, arg3)                                   \
850cb0675eSZhangjin Wu ({                                                                           \
860cb0675eSZhangjin Wu 	register long _ret  __asm__ ("r3");                                  \
870cb0675eSZhangjin Wu 	register long _num  __asm__ ("r0") = (num);                          \
880cb0675eSZhangjin Wu 	register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
890cb0675eSZhangjin Wu 	register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
900cb0675eSZhangjin Wu 	register long _arg3 __asm__ ("r5") = (long)(arg3);                   \
910cb0675eSZhangjin Wu 									     \
920cb0675eSZhangjin Wu 	__asm__ volatile (                                                   \
930cb0675eSZhangjin Wu 		"	sc\n"                                                \
940cb0675eSZhangjin Wu 		"	bns+ 1f\n"                                           \
950cb0675eSZhangjin Wu 		"	neg  %0, %0\n"                                       \
960cb0675eSZhangjin Wu 		"1:\n"                                                       \
970cb0675eSZhangjin Wu 		: "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3)           \
980cb0675eSZhangjin Wu 		: "0"(_arg1)                                                 \
990cb0675eSZhangjin Wu 		: _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6"              \
1000cb0675eSZhangjin Wu 	);                                                                   \
1010cb0675eSZhangjin Wu 	_ret;                                                                \
1020cb0675eSZhangjin Wu })
1030cb0675eSZhangjin Wu 
1040cb0675eSZhangjin Wu 
1050cb0675eSZhangjin Wu #define my_syscall4(num, arg1, arg2, arg3, arg4)                             \
1060cb0675eSZhangjin Wu ({                                                                           \
1070cb0675eSZhangjin Wu 	register long _ret  __asm__ ("r3");                                  \
1080cb0675eSZhangjin Wu 	register long _num  __asm__ ("r0") = (num);                          \
1090cb0675eSZhangjin Wu 	register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
1100cb0675eSZhangjin Wu 	register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
1110cb0675eSZhangjin Wu 	register long _arg3 __asm__ ("r5") = (long)(arg3);                   \
1120cb0675eSZhangjin Wu 	register long _arg4 __asm__ ("r6") = (long)(arg4);                   \
1130cb0675eSZhangjin Wu 									     \
1140cb0675eSZhangjin Wu 	__asm__ volatile (                                                   \
1150cb0675eSZhangjin Wu 		"	sc\n"                                                \
1160cb0675eSZhangjin Wu 		"	bns+ 1f\n"                                           \
1170cb0675eSZhangjin Wu 		"	neg  %0, %0\n"                                       \
1180cb0675eSZhangjin Wu 		"1:\n"                                                       \
1190cb0675eSZhangjin Wu 		: "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3),          \
1200cb0675eSZhangjin Wu 		  "+r"(_arg4)                                                \
1210cb0675eSZhangjin Wu 		: "0"(_arg1)                                                 \
1220cb0675eSZhangjin Wu 		: _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7"                    \
1230cb0675eSZhangjin Wu 	);                                                                   \
1240cb0675eSZhangjin Wu 	_ret;                                                                \
1250cb0675eSZhangjin Wu })
1260cb0675eSZhangjin Wu 
1270cb0675eSZhangjin Wu 
1280cb0675eSZhangjin Wu #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5)                       \
1290cb0675eSZhangjin Wu ({                                                                           \
1300cb0675eSZhangjin Wu 	register long _ret  __asm__ ("r3");                                  \
1310cb0675eSZhangjin Wu 	register long _num  __asm__ ("r0") = (num);                          \
1320cb0675eSZhangjin Wu 	register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
1330cb0675eSZhangjin Wu 	register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
1340cb0675eSZhangjin Wu 	register long _arg3 __asm__ ("r5") = (long)(arg3);                   \
1350cb0675eSZhangjin Wu 	register long _arg4 __asm__ ("r6") = (long)(arg4);                   \
1360cb0675eSZhangjin Wu 	register long _arg5 __asm__ ("r7") = (long)(arg5);                   \
1370cb0675eSZhangjin Wu 									     \
1380cb0675eSZhangjin Wu 	__asm__ volatile (                                                   \
1390cb0675eSZhangjin Wu 		"	sc\n"                                                \
1400cb0675eSZhangjin Wu 		"	bns+ 1f\n"                                           \
1410cb0675eSZhangjin Wu 		"	neg  %0, %0\n"                                       \
1420cb0675eSZhangjin Wu 		"1:\n"                                                       \
1430cb0675eSZhangjin Wu 		: "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3),          \
1440cb0675eSZhangjin Wu 		  "+r"(_arg4), "+r"(_arg5)                                   \
1450cb0675eSZhangjin Wu 		: "0"(_arg1)                                                 \
1460cb0675eSZhangjin Wu 		: _NOLIBC_SYSCALL_CLOBBERLIST, "r8"                          \
1470cb0675eSZhangjin Wu 	);                                                                   \
1480cb0675eSZhangjin Wu 	_ret;                                                                \
1490cb0675eSZhangjin Wu })
1500cb0675eSZhangjin Wu 
1510cb0675eSZhangjin Wu #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)                 \
1520cb0675eSZhangjin Wu ({                                                                           \
1530cb0675eSZhangjin Wu 	register long _ret  __asm__ ("r3");                                  \
1540cb0675eSZhangjin Wu 	register long _num  __asm__ ("r0") = (num);                          \
1550cb0675eSZhangjin Wu 	register long _arg1 __asm__ ("r3") = (long)(arg1);                   \
1560cb0675eSZhangjin Wu 	register long _arg2 __asm__ ("r4") = (long)(arg2);                   \
1570cb0675eSZhangjin Wu 	register long _arg3 __asm__ ("r5") = (long)(arg3);                   \
1580cb0675eSZhangjin Wu 	register long _arg4 __asm__ ("r6") = (long)(arg4);                   \
1590cb0675eSZhangjin Wu 	register long _arg5 __asm__ ("r7") = (long)(arg5);                   \
1600cb0675eSZhangjin Wu 	register long _arg6 __asm__ ("r8") = (long)(arg6);                   \
1610cb0675eSZhangjin Wu 									     \
1620cb0675eSZhangjin Wu 	__asm__ volatile (                                                   \
1630cb0675eSZhangjin Wu 		"	sc\n"                                                \
1640cb0675eSZhangjin Wu 		"	bns+ 1f\n"                                           \
1650cb0675eSZhangjin Wu 		"	neg  %0, %0\n"                                       \
1660cb0675eSZhangjin Wu 		"1:\n"                                                       \
1670cb0675eSZhangjin Wu 		: "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3),          \
1680cb0675eSZhangjin Wu 		  "+r"(_arg4), "+r"(_arg5), "+r"(_arg6)                      \
1690cb0675eSZhangjin Wu 		: "0"(_arg1)                                                 \
1700cb0675eSZhangjin Wu 		: _NOLIBC_SYSCALL_CLOBBERLIST                                \
1710cb0675eSZhangjin Wu 	);                                                                   \
1720cb0675eSZhangjin Wu 	_ret;                                                                \
1730cb0675eSZhangjin Wu })
1740cb0675eSZhangjin Wu 
175*fc975b8dSThomas Weißschuh #if !defined(__powerpc64__) && !defined(__clang__)
1760cb0675eSZhangjin Wu /* FIXME: For 32-bit PowerPC, with newer gcc compilers (e.g. gcc 13.1.0),
1770cb0675eSZhangjin Wu  * "omit-frame-pointer" fails with __attribute__((no_stack_protector)) but
1780cb0675eSZhangjin Wu  * works with __attribute__((__optimize__("-fno-stack-protector")))
1790cb0675eSZhangjin Wu  */
1800cb0675eSZhangjin Wu #ifdef __no_stack_protector
1810cb0675eSZhangjin Wu #undef __no_stack_protector
1820cb0675eSZhangjin Wu #define __no_stack_protector __attribute__((__optimize__("-fno-stack-protector")))
1830cb0675eSZhangjin Wu #endif
184e45ce88eSZhangjin Wu #endif /* !__powerpc64__ */
1850cb0675eSZhangjin Wu 
1860cb0675eSZhangjin Wu /* startup code */
_start(void)1870cb0675eSZhangjin Wu void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
1880cb0675eSZhangjin Wu {
189e45ce88eSZhangjin Wu #ifdef __powerpc64__
190872dbfa0SZhangjin Wu #if _CALL_ELF == 2
191872dbfa0SZhangjin Wu 	/* with -mabi=elfv2, save TOC/GOT pointer to r2
192872dbfa0SZhangjin Wu 	 * r12 is global entry pointer, we use it to compute TOC from r12
193872dbfa0SZhangjin Wu 	 * https://www.llvm.org/devmtg/2014-04/PDFs/Talks/Euro-LLVM-2014-Weigand.pdf
194872dbfa0SZhangjin Wu 	 * https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.pdf
195872dbfa0SZhangjin Wu 	 */
196872dbfa0SZhangjin Wu 	__asm__ volatile (
197872dbfa0SZhangjin Wu 		"addis  2, 12, .TOC. - _start@ha\n"
198872dbfa0SZhangjin Wu 		"addi   2,  2, .TOC. - _start@l\n"
199872dbfa0SZhangjin Wu 	);
200872dbfa0SZhangjin Wu #endif /* _CALL_ELF == 2 */
201e45ce88eSZhangjin Wu 
202e45ce88eSZhangjin Wu 	__asm__ volatile (
203e45ce88eSZhangjin Wu 		"mr     3, 1\n"         /* save stack pointer to r3, as arg1 of _start_c */
204e45ce88eSZhangjin Wu 		"clrrdi 1, 1, 4\n"      /* align the stack to 16 bytes                   */
205e45ce88eSZhangjin Wu 		"li     0, 0\n"         /* zero the frame pointer                        */
206e45ce88eSZhangjin Wu 		"stdu   1, -32(1)\n"    /* the initial stack frame                       */
207e45ce88eSZhangjin Wu 		"bl     _start_c\n"     /* transfer to c runtime                         */
208e45ce88eSZhangjin Wu 	);
209e45ce88eSZhangjin Wu #else
2100cb0675eSZhangjin Wu 	__asm__ volatile (
2110cb0675eSZhangjin Wu 		"mr     3, 1\n"         /* save stack pointer to r3, as arg1 of _start_c */
2120cb0675eSZhangjin Wu 		"clrrwi 1, 1, 4\n"      /* align the stack to 16 bytes                   */
2130cb0675eSZhangjin Wu 		"li     0, 0\n"         /* zero the frame pointer                        */
2140cb0675eSZhangjin Wu 		"stwu   1, -16(1)\n"    /* the initial stack frame                       */
2150cb0675eSZhangjin Wu 		"bl     _start_c\n"     /* transfer to c runtime                         */
2160cb0675eSZhangjin Wu 	);
217e45ce88eSZhangjin Wu #endif
2180cb0675eSZhangjin Wu 	__builtin_unreachable();
2190cb0675eSZhangjin Wu }
2200cb0675eSZhangjin Wu 
2210cb0675eSZhangjin Wu #endif /* _NOLIBC_ARCH_POWERPC_H */
222