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