arch-mips.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
  2. /*
  3. * MIPS specific definitions for NOLIBC
  4. * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
  5. */
  6. #ifndef _NOLIBC_ARCH_MIPS_H
  7. #define _NOLIBC_ARCH_MIPS_H
  8. #include "compiler.h"
  9. #include "crt.h"
  10. #if !defined(_ABIO32)
  11. #error Unsupported MIPS ABI
  12. #endif
  13. /* Syscalls for MIPS ABI O32 :
  14. * - WARNING! there's always a delayed slot!
  15. * - WARNING again, the syntax is different, registers take a '$' and numbers
  16. * do not.
  17. * - registers are 32-bit
  18. * - stack is 8-byte aligned
  19. * - syscall number is passed in v0 (starts at 0xfa0).
  20. * - arguments are in a0, a1, a2, a3, then the stack. The caller needs to
  21. * leave some room in the stack for the callee to save a0..a3 if needed.
  22. * - Many registers are clobbered, in fact only a0..a2 and s0..s8 are
  23. * preserved. See: https://www.linux-mips.org/wiki/Syscall as well as
  24. * scall32-o32.S in the kernel sources.
  25. * - the system call is performed by calling "syscall"
  26. * - syscall return comes in v0, and register a3 needs to be checked to know
  27. * if an error occurred, in which case errno is in v0.
  28. * - the arguments are cast to long and assigned into the target registers
  29. * which are then simply passed as registers to the asm code, so that we
  30. * don't have to experience issues with register constraints.
  31. */
  32. #define _NOLIBC_SYSCALL_CLOBBERLIST \
  33. "memory", "cc", "at", "v1", "hi", "lo", \
  34. "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"
  35. #define my_syscall0(num) \
  36. ({ \
  37. register long _num __asm__ ("v0") = (num); \
  38. register long _arg4 __asm__ ("a3"); \
  39. \
  40. __asm__ volatile ( \
  41. "addiu $sp, $sp, -32\n" \
  42. "syscall\n" \
  43. "addiu $sp, $sp, 32\n" \
  44. : "=r"(_num), "=r"(_arg4) \
  45. : "r"(_num) \
  46. : _NOLIBC_SYSCALL_CLOBBERLIST \
  47. ); \
  48. _arg4 ? -_num : _num; \
  49. })
  50. #define my_syscall1(num, arg1) \
  51. ({ \
  52. register long _num __asm__ ("v0") = (num); \
  53. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  54. register long _arg4 __asm__ ("a3"); \
  55. \
  56. __asm__ volatile ( \
  57. "addiu $sp, $sp, -32\n" \
  58. "syscall\n" \
  59. "addiu $sp, $sp, 32\n" \
  60. : "=r"(_num), "=r"(_arg4) \
  61. : "0"(_num), \
  62. "r"(_arg1) \
  63. : _NOLIBC_SYSCALL_CLOBBERLIST \
  64. ); \
  65. _arg4 ? -_num : _num; \
  66. })
  67. #define my_syscall2(num, arg1, arg2) \
  68. ({ \
  69. register long _num __asm__ ("v0") = (num); \
  70. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  71. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  72. register long _arg4 __asm__ ("a3"); \
  73. \
  74. __asm__ volatile ( \
  75. "addiu $sp, $sp, -32\n" \
  76. "syscall\n" \
  77. "addiu $sp, $sp, 32\n" \
  78. : "=r"(_num), "=r"(_arg4) \
  79. : "0"(_num), \
  80. "r"(_arg1), "r"(_arg2) \
  81. : _NOLIBC_SYSCALL_CLOBBERLIST \
  82. ); \
  83. _arg4 ? -_num : _num; \
  84. })
  85. #define my_syscall3(num, arg1, arg2, arg3) \
  86. ({ \
  87. register long _num __asm__ ("v0") = (num); \
  88. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  89. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  90. register long _arg3 __asm__ ("a2") = (long)(arg3); \
  91. register long _arg4 __asm__ ("a3"); \
  92. \
  93. __asm__ volatile ( \
  94. "addiu $sp, $sp, -32\n" \
  95. "syscall\n" \
  96. "addiu $sp, $sp, 32\n" \
  97. : "=r"(_num), "=r"(_arg4) \
  98. : "0"(_num), \
  99. "r"(_arg1), "r"(_arg2), "r"(_arg3) \
  100. : _NOLIBC_SYSCALL_CLOBBERLIST \
  101. ); \
  102. _arg4 ? -_num : _num; \
  103. })
  104. #define my_syscall4(num, arg1, arg2, arg3, arg4) \
  105. ({ \
  106. register long _num __asm__ ("v0") = (num); \
  107. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  108. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  109. register long _arg3 __asm__ ("a2") = (long)(arg3); \
  110. register long _arg4 __asm__ ("a3") = (long)(arg4); \
  111. \
  112. __asm__ volatile ( \
  113. "addiu $sp, $sp, -32\n" \
  114. "syscall\n" \
  115. "addiu $sp, $sp, 32\n" \
  116. : "=r" (_num), "=r"(_arg4) \
  117. : "0"(_num), \
  118. "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \
  119. : _NOLIBC_SYSCALL_CLOBBERLIST \
  120. ); \
  121. _arg4 ? -_num : _num; \
  122. })
  123. #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
  124. ({ \
  125. register long _num __asm__ ("v0") = (num); \
  126. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  127. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  128. register long _arg3 __asm__ ("a2") = (long)(arg3); \
  129. register long _arg4 __asm__ ("a3") = (long)(arg4); \
  130. register long _arg5 = (long)(arg5); \
  131. \
  132. __asm__ volatile ( \
  133. "addiu $sp, $sp, -32\n" \
  134. "sw %7, 16($sp)\n" \
  135. "syscall\n" \
  136. "addiu $sp, $sp, 32\n" \
  137. : "=r" (_num), "=r"(_arg4) \
  138. : "0"(_num), \
  139. "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \
  140. : _NOLIBC_SYSCALL_CLOBBERLIST \
  141. ); \
  142. _arg4 ? -_num : _num; \
  143. })
  144. #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
  145. ({ \
  146. register long _num __asm__ ("v0") = (num); \
  147. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  148. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  149. register long _arg3 __asm__ ("a2") = (long)(arg3); \
  150. register long _arg4 __asm__ ("a3") = (long)(arg4); \
  151. register long _arg5 = (long)(arg5); \
  152. register long _arg6 = (long)(arg6); \
  153. \
  154. __asm__ volatile ( \
  155. "addiu $sp, $sp, -32\n" \
  156. "sw %7, 16($sp)\n" \
  157. "sw %8, 20($sp)\n" \
  158. "syscall\n" \
  159. "addiu $sp, $sp, 32\n" \
  160. : "=r" (_num), "=r"(_arg4) \
  161. : "0"(_num), \
  162. "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
  163. "r"(_arg6) \
  164. : _NOLIBC_SYSCALL_CLOBBERLIST \
  165. ); \
  166. _arg4 ? -_num : _num; \
  167. })
  168. /* startup code, note that it's called __start on MIPS */
  169. void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector __start(void)
  170. {
  171. __asm__ volatile (
  172. ".set push\n"
  173. ".set noreorder\n"
  174. "bal 1f\n" /* prime $ra for .cpload */
  175. "nop\n"
  176. "1:\n"
  177. ".cpload $ra\n"
  178. "move $a0, $sp\n" /* save stack pointer to $a0, as arg1 of _start_c */
  179. "addiu $sp, $sp, -4\n" /* space for .cprestore to store $gp */
  180. ".cprestore 0\n"
  181. "li $t0, -8\n"
  182. "and $sp, $sp, $t0\n" /* $sp must be 8-byte aligned */
  183. "addiu $sp, $sp, -16\n" /* the callee expects to save a0..a3 there */
  184. "lui $t9, %hi(_start_c)\n" /* ABI requires current function address in $t9 */
  185. "ori $t9, %lo(_start_c)\n"
  186. "jalr $t9\n" /* transfer to c runtime */
  187. " nop\n" /* delayed slot */
  188. ".set pop\n"
  189. );
  190. __nolibc_entrypoint_epilogue();
  191. }
  192. #endif /* _NOLIBC_ARCH_MIPS_H */