| 1 | /* Assembler macros for ARC. |
| 2 | Copyright (C) 2020-2026 Free Software Foundation, Inc. |
| 3 | This file is part of the GNU C Library. |
| 4 | |
| 5 | The GNU C Library is free software; you can redistribute it and/or |
| 6 | modify it under the terms of the GNU Lesser General Public |
| 7 | License as published by the Free Software Foundation; either |
| 8 | version 2.1 of the License, or (at your option) any later version. |
| 9 | |
| 10 | The GNU C Library is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | Lesser General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU Lesser General Public |
| 16 | License along with the GNU C Library; if not, see |
| 17 | <https://www.gnu.org/licenses/>. */ |
| 18 | |
| 19 | #ifndef _LINUX_ARC_SYSDEP_H |
| 20 | #define _LINUX_ARC_SYSDEP_H 1 |
| 21 | |
| 22 | #include <sysdeps/arc/sysdep.h> |
| 23 | #include <bits/wordsize.h> |
| 24 | #include <sysdeps/unix/sysdep.h> |
| 25 | #include <sysdeps/unix/sysv/linux/sysdep.h> |
| 26 | |
| 27 | /* "workarounds" for generic code needing to handle 64-bit time_t. */ |
| 28 | |
| 29 | /* Fix sysdeps/unix/sysv/linux/clock_getcpuclockid.c. */ |
| 30 | #define __NR_clock_getres	__NR_clock_getres_time64 |
| 31 | /* Fix sysdeps/nptl/lowlevellock-futex.h. */ |
| 32 | #define __NR_futex		__NR_futex_time64 |
| 33 | /* Fix sysdeps/unix/sysv/linux/pause.c. */ |
| 34 | #define __NR_ppoll		__NR_ppoll_time64 |
| 35 | /* Fix sysdeps/unix/sysv/linux/select.c. */ |
| 36 | #define __NR_pselect6		__NR_pselect6_time64 |
| 37 | /* Fix sysdeps/unix/sysv/linux/recvmmsg.c. */ |
| 38 | #define __NR_recvmmsg		__NR_recvmmsg_time64 |
| 39 | /* Fix sysdeps/unix/sysv/linux/sigtimedwait.c. */ |
| 40 | #define __NR_rt_sigtimedwait	__NR_rt_sigtimedwait_time64 |
| 41 | /* Fix sysdeps/unix/sysv/linux/semtimedop.c. */ |
| 42 | #define __NR_semtimedop		__NR_semtimedop_time64 |
| 43 | /* Hack sysdeps/unix/sysv/linux/generic/utimes.c. */ |
| 44 | #define __NR_utimensat		__NR_utimensat_time64 |
| 45 | |
| 46 | /* For RTLD_PRIVATE_ERRNO. */ |
| 47 | #include <dl-sysdep.h> |
| 48 | |
| 49 | #include <tls.h> |
| 50 | |
| 51 | #undef SYS_ify |
| 52 | #define SYS_ify(syscall_name) __NR_##syscall_name |
| 53 | |
| 54 | #ifdef __ASSEMBLER__ |
| 55 | |
| 56 | /* This is a "normal" system call stub: if there is an error, |
| 57 | it returns -1 and sets errno. */ |
| 58 | |
| 59 | # undef PSEUDO |
| 60 | # define PSEUDO(name, syscall_name, args)			\ |
| 61 | PSEUDO_NOERRNO(name, syscall_name, args)	ASM_LINE_SEP	\ |
| 62 | brhi r0, -4096, L (call_syscall_err)	ASM_LINE_SEP |
| 63 | |
| 64 | # define ret	j_s [blink] |
| 65 | |
| 66 | # undef PSEUDO_END |
| 67 | # define PSEUDO_END(name)					\ |
| 68 | SYSCALL_ERROR_HANDLER				ASM_LINE_SEP	\ |
| 69 | END (name) |
| 70 | |
| 71 | /* --------- Helper for SYSCALL_NOERRNO ----------- |
| 72 | This kind of system call stub never returns an error. |
| 73 | We return the return value register to the caller unexamined. */ |
| 74 | |
| 75 | # undef PSEUDO_NOERRNO |
| 76 | # define PSEUDO_NOERRNO(name, syscall_name, args)		\ |
| 77 | .text						ASM_LINE_SEP	\ |
| 78 | ENTRY (name)					ASM_LINE_SEP	\ |
| 79 | DO_CALL (syscall_name, args)		ASM_LINE_SEP	\ |
| 80 | |
| 81 | /* Return the return value register unexamined. Since r0 is both |
| 82 | syscall return reg and function return reg, no work needed. */ |
| 83 | # define ret_NOERRNO						\ |
| 84 | j_s [blink]		ASM_LINE_SEP |
| 85 | |
| 86 | # undef PSEUDO_END_NOERRNO |
| 87 | # define PSEUDO_END_NOERRNO(name)				\ |
| 88 | END (name) |
| 89 | |
| 90 | /* --------- Helper for SYSCALL_ERRVAL ----------- |
| 91 | This kind of system call stub returns the errno code as its return |
| 92 | value, or zero for success. We may massage the kernel's return value |
| 93 | to meet that ABI, but we never set errno here. */ |
| 94 | |
| 95 | # undef PSEUDO_ERRVAL |
| 96 | # define PSEUDO_ERRVAL(name, syscall_name, args)		\ |
| 97 | PSEUDO_NOERRNO(name, syscall_name, args)	ASM_LINE_SEP |
| 98 | |
| 99 | /* Don't set errno, return kernel error (in errno form) or zero. */ |
| 100 | # define ret_ERRVAL						\ |
| 101 | rsub r0, r0, 0				ASM_LINE_SEP	\ |
| 102 | ret_NOERRNO |
| 103 | |
| 104 | # undef PSEUDO_END_ERRVAL |
| 105 | # define PSEUDO_END_ERRVAL(name)				\ |
| 106 | END (name) |
| 107 | |
| 108 | |
| 109 | /* To reduce the code footprint, we confine the actual errno access |
| 110 | to single place in __syscall_error(). |
| 111 | This takes raw kernel error value, sets errno and returns -1. */ |
| 112 | # if IS_IN (libc) |
| 113 | # define CALL_ERRNO_SETTER_C	bl PLTJMP(HIDDEN_JUMPTARGET(__syscall_error)) |
| 114 | # else |
| 115 | # define CALL_ERRNO_SETTER_C	bl PLTJMP(__syscall_error) |
| 116 | # endif |
| 117 | |
| 118 | # define SYSCALL_ERROR_HANDLER				\ |
| 119 | L (call_syscall_err):			ASM_LINE_SEP	\ |
| 120 | push_s blink			ASM_LINE_SEP	\ |
| 121 | cfi_adjust_cfa_offset (4)		ASM_LINE_SEP	\ |
| 122 | cfi_rel_offset (blink, 0)		ASM_LINE_SEP	\ |
| 123 | CALL_ERRNO_SETTER_C			ASM_LINE_SEP	\ |
| 124 | pop_s blink			ASM_LINE_SEP	\ |
| 125 | cfi_adjust_cfa_offset (-4)		ASM_LINE_SEP	\ |
| 126 | cfi_restore (blink)			ASM_LINE_SEP	\ |
| 127 | j_s [blink] |
| 128 | |
| 129 | # define DO_CALL(syscall_name, args)			\ |
| 130 | mov r8, __NR_##syscall_name	ASM_LINE_SEP	\ |
| 131 | ARC_TRAP_INSN			ASM_LINE_SEP |
| 132 | |
| 133 | # define ARC_TRAP_INSN	trap_s 0 |
| 134 | |
| 135 | #else /* !__ASSEMBLER__ */ |
| 136 | |
| 137 | # if IS_IN (libc) |
| 138 | extern long int __syscall_error (long int); |
| 139 | hidden_proto (__syscall_error) |
| 140 | # endif |
| 141 | |
| 142 | # define ARC_TRAP_INSN	"trap_s 0	\n\t" |
| 143 | |
| 144 | # define HAVE_CLONE3_WRAPPER	1 |
| 145 | |
| 146 | # undef INTERNAL_SYSCALL_NCS |
| 147 | # define INTERNAL_SYSCALL_NCS(number, nr_args, args...)	\ |
| 148 | ({								\ |
| 149 | /* Per ABI, r0 is 1st arg and return reg. */		\ |
| 150 | register long int __ret __asm__("r0");			\ |
| 151 | register long int _sys_num __asm__("r8");			\ |
| 152 | 								\ |
| 153 | LOAD_ARGS_##nr_args (number, args)				\ |
| 154 | 								\ |
| 155 | __asm__ volatile (						\ |
| 156 | ARC_TRAP_INSN				\ |
| 157 | : "+r" (__ret)				\ |
| 158 | : "r"(_sys_num) ASM_ARGS_##nr_args	\ |
| 159 | : "memory");				\ |
| 160 | \ |
| 161 | __ret; }) |
| 162 | |
| 163 | # undef INTERNAL_SYSCALL |
| 164 | # define INTERNAL_SYSCALL(name, nr, args...) 	\ |
| 165 | INTERNAL_SYSCALL_NCS(__NR_##name, nr, args) |
| 166 | |
| 167 | /* Macros for setting up inline __asm__ input regs. */ |
| 168 | # define ASM_ARGS_0 |
| 169 | # define ASM_ARGS_1	ASM_ARGS_0, "r" (__ret) |
| 170 | # define ASM_ARGS_2	ASM_ARGS_1, "r" (_arg2) |
| 171 | # define ASM_ARGS_3	ASM_ARGS_2, "r" (_arg3) |
| 172 | # define ASM_ARGS_4	ASM_ARGS_3, "r" (_arg4) |
| 173 | # define ASM_ARGS_5	ASM_ARGS_4, "r" (_arg5) |
| 174 | # define ASM_ARGS_6	ASM_ARGS_5, "r" (_arg6) |
| 175 | # define ASM_ARGS_7	ASM_ARGS_6, "r" (_arg7) |
| 176 | |
| 177 | /* Macros for converting sys-call wrapper args into sys call args. */ |
| 178 | # define LOAD_ARGS_0(nm, arg)				\ |
| 179 | _sys_num = (long int) (nm); |
| 180 | |
| 181 | # define LOAD_ARGS_1(nm, arg1)				\ |
| 182 | __ret = (long int) (arg1);					\ |
| 183 | LOAD_ARGS_0 (nm, arg1) |
| 184 | |
| 185 | /* Note that the use of _tmpX might look superfluous, however it is needed |
| 186 | to ensure that register variables are not clobbered if arg happens to be |
| 187 | a function call itself. e.g. sched_setaffinity() calling getpid() for arg2 |
| 188 | Also this specific order of recursive calling is important to segregate |
| 189 | the tmp args evaluation (function call case described above) and assignment |
| 190 | of register variables. */ |
| 191 | |
| 192 | # define LOAD_ARGS_2(nm, arg1, arg2)			\ |
| 193 | long int _tmp2 = (long int) (arg2);			\ |
| 194 | LOAD_ARGS_1 (nm, arg1)				\ |
| 195 | register long int _arg2 __asm__ ("r1") = _tmp2; |
| 196 | |
| 197 | # define LOAD_ARGS_3(nm, arg1, arg2, arg3)		\ |
| 198 | long int _tmp3 = (long int) (arg3);			\ |
| 199 | LOAD_ARGS_2 (nm, arg1, arg2)				\ |
| 200 | register long int _arg3 __asm__ ("r2") = _tmp3; |
| 201 | |
| 202 | #define LOAD_ARGS_4(nm, arg1, arg2, arg3, arg4)		\ |
| 203 | long int _tmp4 = (long int) (arg4);			\ |
| 204 | LOAD_ARGS_3 (nm, arg1, arg2, arg3)			\ |
| 205 | register long int _arg4 __asm__ ("r3") = _tmp4; |
| 206 | |
| 207 | # define LOAD_ARGS_5(nm, arg1, arg2, arg3, arg4, arg5)	\ |
| 208 | long int _tmp5 = (long int) (arg5);			\ |
| 209 | LOAD_ARGS_4 (nm, arg1, arg2, arg3, arg4)		\ |
| 210 | register long int _arg5 __asm__ ("r4") = _tmp5; |
| 211 | |
| 212 | # define LOAD_ARGS_6(nm, arg1, arg2, arg3, arg4, arg5, arg6)\ |
| 213 | long int _tmp6 = (long int) (arg6);			\ |
| 214 | LOAD_ARGS_5 (nm, arg1, arg2, arg3, arg4, arg5)	\ |
| 215 | register long int _arg6 __asm__ ("r5") = _tmp6; |
| 216 | |
| 217 | # define LOAD_ARGS_7(nm, arg1, arg2, arg3, arg4, arg5, arg6, arg7)\ |
| 218 | long int _tmp7 = (int) (arg7);				\ |
| 219 | LOAD_ARGS_6 (nm, arg1, arg2, arg3, arg4, arg5, arg6)	\ |
| 220 | register long int _arg7 __asm__ ("r6") = _tmp7; |
| 221 | |
| 222 | # undef HAVE_INTERNAL_BRK_ADDR_SYMBOL |
| 223 | # define HAVE_INTERNAL_BRK_ADDR_SYMBOL 1 |
| 224 | |
| 225 | #endif /* !__ASSEMBLER__ */ |
| 226 | |
| 227 | #endif /* linux/arc/sysdep.h */ |