|  | /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ | 
|  | /* | 
|  | * PowerPC specific definitions for NOLIBC | 
|  | * Copyright (C) 2023 Zhangjin Wu <falcon@tinylab.org> | 
|  | */ | 
|  |  | 
|  | #ifndef _NOLIBC_ARCH_POWERPC_H | 
|  | #define _NOLIBC_ARCH_POWERPC_H | 
|  |  | 
|  | #include "compiler.h" | 
|  | #include "crt.h" | 
|  |  | 
|  | /* Syscalls for PowerPC : | 
|  | *   - stack is 16-byte aligned | 
|  | *   - syscall number is passed in r0 | 
|  | *   - arguments are in r3, r4, r5, r6, r7, r8, r9 | 
|  | *   - the system call is performed by calling "sc" | 
|  | *   - syscall return comes in r3, and the summary overflow bit is checked | 
|  | *     to know if an error occurred, in which case errno is in r3. | 
|  | *   - the arguments are cast to long and assigned into the target | 
|  | *     registers which are then simply passed as registers to the asm code, | 
|  | *     so that we don't have to experience issues with register constraints. | 
|  | */ | 
|  |  | 
|  | #define _NOLIBC_SYSCALL_CLOBBERLIST \ | 
|  | "memory", "cr0", "r12", "r11", "r10", "r9" | 
|  |  | 
|  | #define my_syscall0(num)                                                     \ | 
|  | ({                                                                           \ | 
|  | register long _ret  __asm__ ("r3");                                  \ | 
|  | register long _num  __asm__ ("r0") = (num);                          \ | 
|  | \ | 
|  | __asm__ volatile (                                                   \ | 
|  | "	sc\n"                                                \ | 
|  | "	bns+ 1f\n"                                           \ | 
|  | "	neg  %0, %0\n"                                       \ | 
|  | "1:\n"                                                       \ | 
|  | : "=r"(_ret), "+r"(_num)                                     \ | 
|  | :                                                            \ | 
|  | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5", "r4"  \ | 
|  | );                                                                   \ | 
|  | _ret;                                                                \ | 
|  | }) | 
|  |  | 
|  | #define my_syscall1(num, arg1)                                               \ | 
|  | ({                                                                           \ | 
|  | register long _ret  __asm__ ("r3");                                  \ | 
|  | register long _num  __asm__ ("r0") = (num);                          \ | 
|  | register long _arg1 __asm__ ("r3") = (long)(arg1);                   \ | 
|  | \ | 
|  | __asm__ volatile (                                                   \ | 
|  | "	sc\n"                                                \ | 
|  | "	bns+ 1f\n"                                           \ | 
|  | "	neg  %0, %0\n"                                       \ | 
|  | "1:\n"                                                       \ | 
|  | : "=r"(_ret), "+r"(_num)                                     \ | 
|  | : "0"(_arg1)                                                 \ | 
|  | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5", "r4"  \ | 
|  | );                                                                   \ | 
|  | _ret;                                                                \ | 
|  | }) | 
|  |  | 
|  |  | 
|  | #define my_syscall2(num, arg1, arg2)                                         \ | 
|  | ({                                                                           \ | 
|  | register long _ret  __asm__ ("r3");                                  \ | 
|  | register long _num  __asm__ ("r0") = (num);                          \ | 
|  | register long _arg1 __asm__ ("r3") = (long)(arg1);                   \ | 
|  | register long _arg2 __asm__ ("r4") = (long)(arg2);                   \ | 
|  | \ | 
|  | __asm__ volatile (                                                   \ | 
|  | "	sc\n"                                                \ | 
|  | "	bns+ 1f\n"                                           \ | 
|  | "	neg  %0, %0\n"                                       \ | 
|  | "1:\n"                                                       \ | 
|  | : "=r"(_ret), "+r"(_num), "+r"(_arg2)                        \ | 
|  | : "0"(_arg1)                                                 \ | 
|  | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6", "r5"        \ | 
|  | );                                                                   \ | 
|  | _ret;                                                                \ | 
|  | }) | 
|  |  | 
|  |  | 
|  | #define my_syscall3(num, arg1, arg2, arg3)                                   \ | 
|  | ({                                                                           \ | 
|  | register long _ret  __asm__ ("r3");                                  \ | 
|  | register long _num  __asm__ ("r0") = (num);                          \ | 
|  | register long _arg1 __asm__ ("r3") = (long)(arg1);                   \ | 
|  | register long _arg2 __asm__ ("r4") = (long)(arg2);                   \ | 
|  | register long _arg3 __asm__ ("r5") = (long)(arg3);                   \ | 
|  | \ | 
|  | __asm__ volatile (                                                   \ | 
|  | "	sc\n"                                                \ | 
|  | "	bns+ 1f\n"                                           \ | 
|  | "	neg  %0, %0\n"                                       \ | 
|  | "1:\n"                                                       \ | 
|  | : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3)           \ | 
|  | : "0"(_arg1)                                                 \ | 
|  | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7", "r6"              \ | 
|  | );                                                                   \ | 
|  | _ret;                                                                \ | 
|  | }) | 
|  |  | 
|  |  | 
|  | #define my_syscall4(num, arg1, arg2, arg3, arg4)                             \ | 
|  | ({                                                                           \ | 
|  | register long _ret  __asm__ ("r3");                                  \ | 
|  | register long _num  __asm__ ("r0") = (num);                          \ | 
|  | register long _arg1 __asm__ ("r3") = (long)(arg1);                   \ | 
|  | register long _arg2 __asm__ ("r4") = (long)(arg2);                   \ | 
|  | register long _arg3 __asm__ ("r5") = (long)(arg3);                   \ | 
|  | register long _arg4 __asm__ ("r6") = (long)(arg4);                   \ | 
|  | \ | 
|  | __asm__ volatile (                                                   \ | 
|  | "	sc\n"                                                \ | 
|  | "	bns+ 1f\n"                                           \ | 
|  | "	neg  %0, %0\n"                                       \ | 
|  | "1:\n"                                                       \ | 
|  | : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3),          \ | 
|  | "+r"(_arg4)                                                \ | 
|  | : "0"(_arg1)                                                 \ | 
|  | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8", "r7"                    \ | 
|  | );                                                                   \ | 
|  | _ret;                                                                \ | 
|  | }) | 
|  |  | 
|  |  | 
|  | #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5)                       \ | 
|  | ({                                                                           \ | 
|  | register long _ret  __asm__ ("r3");                                  \ | 
|  | register long _num  __asm__ ("r0") = (num);                          \ | 
|  | register long _arg1 __asm__ ("r3") = (long)(arg1);                   \ | 
|  | register long _arg2 __asm__ ("r4") = (long)(arg2);                   \ | 
|  | register long _arg3 __asm__ ("r5") = (long)(arg3);                   \ | 
|  | register long _arg4 __asm__ ("r6") = (long)(arg4);                   \ | 
|  | register long _arg5 __asm__ ("r7") = (long)(arg5);                   \ | 
|  | \ | 
|  | __asm__ volatile (                                                   \ | 
|  | "	sc\n"                                                \ | 
|  | "	bns+ 1f\n"                                           \ | 
|  | "	neg  %0, %0\n"                                       \ | 
|  | "1:\n"                                                       \ | 
|  | : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3),          \ | 
|  | "+r"(_arg4), "+r"(_arg5)                                   \ | 
|  | : "0"(_arg1)                                                 \ | 
|  | : _NOLIBC_SYSCALL_CLOBBERLIST, "r8"                          \ | 
|  | );                                                                   \ | 
|  | _ret;                                                                \ | 
|  | }) | 
|  |  | 
|  | #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)                 \ | 
|  | ({                                                                           \ | 
|  | register long _ret  __asm__ ("r3");                                  \ | 
|  | register long _num  __asm__ ("r0") = (num);                          \ | 
|  | register long _arg1 __asm__ ("r3") = (long)(arg1);                   \ | 
|  | register long _arg2 __asm__ ("r4") = (long)(arg2);                   \ | 
|  | register long _arg3 __asm__ ("r5") = (long)(arg3);                   \ | 
|  | register long _arg4 __asm__ ("r6") = (long)(arg4);                   \ | 
|  | register long _arg5 __asm__ ("r7") = (long)(arg5);                   \ | 
|  | register long _arg6 __asm__ ("r8") = (long)(arg6);                   \ | 
|  | \ | 
|  | __asm__ volatile (                                                   \ | 
|  | "	sc\n"                                                \ | 
|  | "	bns+ 1f\n"                                           \ | 
|  | "	neg  %0, %0\n"                                       \ | 
|  | "1:\n"                                                       \ | 
|  | : "=r"(_ret), "+r"(_num), "+r"(_arg2), "+r"(_arg3),          \ | 
|  | "+r"(_arg4), "+r"(_arg5), "+r"(_arg6)                      \ | 
|  | : "0"(_arg1)                                                 \ | 
|  | : _NOLIBC_SYSCALL_CLOBBERLIST                                \ | 
|  | );                                                                   \ | 
|  | _ret;                                                                \ | 
|  | }) | 
|  |  | 
|  | #if !defined(__powerpc64__) && !defined(__clang__) | 
|  | /* FIXME: For 32-bit PowerPC, with newer gcc compilers (e.g. gcc 13.1.0), | 
|  | * "omit-frame-pointer" fails with __attribute__((no_stack_protector)) but | 
|  | * works with __attribute__((__optimize__("-fno-stack-protector"))) | 
|  | */ | 
|  | #ifdef __no_stack_protector | 
|  | #undef __no_stack_protector | 
|  | #define __no_stack_protector __attribute__((__optimize__("-fno-stack-protector"))) | 
|  | #endif | 
|  | #endif /* !__powerpc64__ */ | 
|  |  | 
|  | /* startup code */ | 
|  | void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void) | 
|  | { | 
|  | #ifdef __powerpc64__ | 
|  | #if _CALL_ELF == 2 | 
|  | /* with -mabi=elfv2, save TOC/GOT pointer to r2 | 
|  | * r12 is global entry pointer, we use it to compute TOC from r12 | 
|  | * https://www.llvm.org/devmtg/2014-04/PDFs/Talks/Euro-LLVM-2014-Weigand.pdf | 
|  | * https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.pdf | 
|  | */ | 
|  | __asm__ volatile ( | 
|  | "addis  2, 12, .TOC. - _start@ha\n" | 
|  | "addi   2,  2, .TOC. - _start@l\n" | 
|  | ); | 
|  | #endif /* _CALL_ELF == 2 */ | 
|  |  | 
|  | __asm__ volatile ( | 
|  | "mr     3, 1\n"         /* save stack pointer to r3, as arg1 of _start_c */ | 
|  | "clrrdi 1, 1, 4\n"      /* align the stack to 16 bytes                   */ | 
|  | "li     0, 0\n"         /* zero the frame pointer                        */ | 
|  | "stdu   1, -32(1)\n"    /* the initial stack frame                       */ | 
|  | "bl     _start_c\n"     /* transfer to c runtime                         */ | 
|  | ); | 
|  | #else | 
|  | __asm__ volatile ( | 
|  | "mr     3, 1\n"         /* save stack pointer to r3, as arg1 of _start_c */ | 
|  | "clrrwi 1, 1, 4\n"      /* align the stack to 16 bytes                   */ | 
|  | "li     0, 0\n"         /* zero the frame pointer                        */ | 
|  | "stwu   1, -16(1)\n"    /* the initial stack frame                       */ | 
|  | "bl     _start_c\n"     /* transfer to c runtime                         */ | 
|  | ); | 
|  | #endif | 
|  | __builtin_unreachable(); | 
|  | } | 
|  |  | 
|  | #endif /* _NOLIBC_ARCH_POWERPC_H */ |