1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2 /* 3 * LoongArch specific definitions for NOLIBC 4 * Copyright (C) 2023 Loongson Technology Corporation Limited 5 */ 6 7 #ifndef _NOLIBC_ARCH_LOONGARCH_H 8 #define _NOLIBC_ARCH_LOONGARCH_H 9 10 #include "compiler.h" 11 12 /* Syscalls for LoongArch : 13 * - stack is 16-byte aligned 14 * - syscall number is passed in a7 15 * - arguments are in a0, a1, a2, a3, a4, a5 16 * - the system call is performed by calling "syscall 0" 17 * - syscall return comes in a0 18 * - the arguments are cast to long and assigned into the target 19 * registers which are then simply passed as registers to the asm code, 20 * so that we don't have to experience issues with register constraints. 21 * 22 * On LoongArch, select() is not implemented so we have to use pselect6(). 23 */ 24 #define __ARCH_WANT_SYS_PSELECT6 25 26 #define my_syscall0(num) \ 27 ({ \ 28 register long _num __asm__ ("a7") = (num); \ 29 register long _arg1 __asm__ ("a0"); \ 30 \ 31 __asm__ volatile ( \ 32 "syscall 0\n" \ 33 : "=r"(_arg1) \ 34 : "r"(_num) \ 35 : "memory", "$t0", "$t1", "$t2", "$t3", \ 36 "$t4", "$t5", "$t6", "$t7", "$t8" \ 37 ); \ 38 _arg1; \ 39 }) 40 41 #define my_syscall1(num, arg1) \ 42 ({ \ 43 register long _num __asm__ ("a7") = (num); \ 44 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 45 \ 46 __asm__ volatile ( \ 47 "syscall 0\n" \ 48 : "+r"(_arg1) \ 49 : "r"(_num) \ 50 : "memory", "$t0", "$t1", "$t2", "$t3", \ 51 "$t4", "$t5", "$t6", "$t7", "$t8" \ 52 ); \ 53 _arg1; \ 54 }) 55 56 #define my_syscall2(num, arg1, arg2) \ 57 ({ \ 58 register long _num __asm__ ("a7") = (num); \ 59 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 60 register long _arg2 __asm__ ("a1") = (long)(arg2); \ 61 \ 62 __asm__ volatile ( \ 63 "syscall 0\n" \ 64 : "+r"(_arg1) \ 65 : "r"(_arg2), \ 66 "r"(_num) \ 67 : "memory", "$t0", "$t1", "$t2", "$t3", \ 68 "$t4", "$t5", "$t6", "$t7", "$t8" \ 69 ); \ 70 _arg1; \ 71 }) 72 73 #define my_syscall3(num, arg1, arg2, arg3) \ 74 ({ \ 75 register long _num __asm__ ("a7") = (num); \ 76 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 77 register long _arg2 __asm__ ("a1") = (long)(arg2); \ 78 register long _arg3 __asm__ ("a2") = (long)(arg3); \ 79 \ 80 __asm__ volatile ( \ 81 "syscall 0\n" \ 82 : "+r"(_arg1) \ 83 : "r"(_arg2), "r"(_arg3), \ 84 "r"(_num) \ 85 : "memory", "$t0", "$t1", "$t2", "$t3", \ 86 "$t4", "$t5", "$t6", "$t7", "$t8" \ 87 ); \ 88 _arg1; \ 89 }) 90 91 #define my_syscall4(num, arg1, arg2, arg3, arg4) \ 92 ({ \ 93 register long _num __asm__ ("a7") = (num); \ 94 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 95 register long _arg2 __asm__ ("a1") = (long)(arg2); \ 96 register long _arg3 __asm__ ("a2") = (long)(arg3); \ 97 register long _arg4 __asm__ ("a3") = (long)(arg4); \ 98 \ 99 __asm__ volatile ( \ 100 "syscall 0\n" \ 101 : "+r"(_arg1) \ 102 : "r"(_arg2), "r"(_arg3), "r"(_arg4), \ 103 "r"(_num) \ 104 : "memory", "$t0", "$t1", "$t2", "$t3", \ 105 "$t4", "$t5", "$t6", "$t7", "$t8" \ 106 ); \ 107 _arg1; \ 108 }) 109 110 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ 111 ({ \ 112 register long _num __asm__ ("a7") = (num); \ 113 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 114 register long _arg2 __asm__ ("a1") = (long)(arg2); \ 115 register long _arg3 __asm__ ("a2") = (long)(arg3); \ 116 register long _arg4 __asm__ ("a3") = (long)(arg4); \ 117 register long _arg5 __asm__ ("a4") = (long)(arg5); \ 118 \ 119 __asm__ volatile ( \ 120 "syscall 0\n" \ 121 : "+r"(_arg1) \ 122 : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ 123 "r"(_num) \ 124 : "memory", "$t0", "$t1", "$t2", "$t3", \ 125 "$t4", "$t5", "$t6", "$t7", "$t8" \ 126 ); \ 127 _arg1; \ 128 }) 129 130 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ 131 ({ \ 132 register long _num __asm__ ("a7") = (num); \ 133 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 134 register long _arg2 __asm__ ("a1") = (long)(arg2); \ 135 register long _arg3 __asm__ ("a2") = (long)(arg3); \ 136 register long _arg4 __asm__ ("a3") = (long)(arg4); \ 137 register long _arg5 __asm__ ("a4") = (long)(arg5); \ 138 register long _arg6 __asm__ ("a5") = (long)(arg6); \ 139 \ 140 __asm__ volatile ( \ 141 "syscall 0\n" \ 142 : "+r"(_arg1) \ 143 : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_arg6), \ 144 "r"(_num) \ 145 : "memory", "$t0", "$t1", "$t2", "$t3", \ 146 "$t4", "$t5", "$t6", "$t7", "$t8" \ 147 ); \ 148 _arg1; \ 149 }) 150 151 char **environ __attribute__((weak)); 152 const unsigned long *_auxv __attribute__((weak)); 153 154 #if __loongarch_grlen == 32 155 #define LONGLOG "2" 156 #define SZREG "4" 157 #define REG_L "ld.w" 158 #define LONG_S "st.w" 159 #define LONG_ADD "add.w" 160 #define LONG_ADDI "addi.w" 161 #define LONG_SLL "slli.w" 162 #define LONG_BSTRINS "bstrins.w" 163 #else /* __loongarch_grlen == 64 */ 164 #define LONGLOG "3" 165 #define SZREG "8" 166 #define REG_L "ld.d" 167 #define LONG_S "st.d" 168 #define LONG_ADD "add.d" 169 #define LONG_ADDI "addi.d" 170 #define LONG_SLL "slli.d" 171 #define LONG_BSTRINS "bstrins.d" 172 #endif 173 174 /* startup code */ 175 void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void) 176 { 177 __asm__ volatile ( 178 #ifdef _NOLIBC_STACKPROTECTOR 179 "bl __stack_chk_init\n" /* initialize stack protector */ 180 #endif 181 REG_L " $a0, $sp, 0\n" /* argc (a0) was in the stack */ 182 LONG_ADDI " $a1, $sp, "SZREG"\n" /* argv (a1) = sp + SZREG */ 183 LONG_SLL " $a2, $a0, "LONGLOG"\n" /* envp (a2) = SZREG*argc ... */ 184 LONG_ADDI " $a2, $a2, "SZREG"\n" /* + SZREG (skip null) */ 185 LONG_ADD " $a2, $a2, $a1\n" /* + argv */ 186 187 "move $a3, $a2\n" /* iterate a3 over envp to find auxv (after NULL) */ 188 "0:\n" /* do { */ 189 REG_L " $a4, $a3, 0\n" /* a4 = *a3; */ 190 LONG_ADDI " $a3, $a3, "SZREG"\n" /* a3 += sizeof(void*); */ 191 "bne $a4, $zero, 0b\n" /* } while (a4); */ 192 "la.pcrel $a4, _auxv\n" /* a4 = &_auxv */ 193 LONG_S " $a3, $a4, 0\n" /* store a3 into _auxv */ 194 195 "la.pcrel $a3, environ\n" /* a3 = &environ */ 196 LONG_S " $a2, $a3, 0\n" /* store envp(a2) into environ */ 197 LONG_BSTRINS " $sp, $zero, 3, 0\n" /* sp must be 16-byte aligned */ 198 "bl main\n" /* main() returns the status code, we'll exit with it. */ 199 "li.w $a7, 93\n" /* NR_exit == 93 */ 200 "syscall 0\n" 201 ); 202 __builtin_unreachable(); 203 } 204 205 #endif /* _NOLIBC_ARCH_LOONGARCH_H */ 206