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