arch-mips.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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) && !defined(_ABIN32) && !defined(_ABI64)
  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. * Syscalls for MIPS ABI N32, same as ABI O32 with the following differences :
  33. * - arguments are in a0, a1, a2, a3, t0, t1, t2, t3.
  34. * t0..t3 are also known as a4..a7.
  35. * - stack is 16-byte aligned
  36. */
  37. #if defined(_ABIO32)
  38. #define _NOLIBC_SYSCALL_CLOBBERLIST \
  39. "memory", "cc", "at", "v1", "hi", "lo", \
  40. "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"
  41. #define _NOLIBC_SYSCALL_STACK_RESERVE "addiu $sp, $sp, -32\n"
  42. #define _NOLIBC_SYSCALL_STACK_UNRESERVE "addiu $sp, $sp, 32\n"
  43. #else /* _ABIN32 || _ABI64 */
  44. /* binutils, GCC and clang disagree about register aliases, use numbers instead. */
  45. #define _NOLIBC_SYSCALL_CLOBBERLIST \
  46. "memory", "cc", "at", "v1", \
  47. "10", "11", "12", "13", "14", "15", "24", "25"
  48. #define _NOLIBC_SYSCALL_STACK_RESERVE
  49. #define _NOLIBC_SYSCALL_STACK_UNRESERVE
  50. #endif /* _ABIO32 */
  51. #define my_syscall0(num) \
  52. ({ \
  53. register long _num __asm__ ("v0") = (num); \
  54. register long _arg4 __asm__ ("a3"); \
  55. \
  56. __asm__ volatile ( \
  57. _NOLIBC_SYSCALL_STACK_RESERVE \
  58. "syscall\n" \
  59. _NOLIBC_SYSCALL_STACK_UNRESERVE \
  60. : "=r"(_num), "=r"(_arg4) \
  61. : "r"(_num) \
  62. : _NOLIBC_SYSCALL_CLOBBERLIST \
  63. ); \
  64. _arg4 ? -_num : _num; \
  65. })
  66. #define my_syscall1(num, arg1) \
  67. ({ \
  68. register long _num __asm__ ("v0") = (num); \
  69. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  70. register long _arg4 __asm__ ("a3"); \
  71. \
  72. __asm__ volatile ( \
  73. _NOLIBC_SYSCALL_STACK_RESERVE \
  74. "syscall\n" \
  75. _NOLIBC_SYSCALL_STACK_UNRESERVE \
  76. : "=r"(_num), "=r"(_arg4) \
  77. : "0"(_num), \
  78. "r"(_arg1) \
  79. : _NOLIBC_SYSCALL_CLOBBERLIST \
  80. ); \
  81. _arg4 ? -_num : _num; \
  82. })
  83. #define my_syscall2(num, arg1, arg2) \
  84. ({ \
  85. register long _num __asm__ ("v0") = (num); \
  86. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  87. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  88. register long _arg4 __asm__ ("a3"); \
  89. \
  90. __asm__ volatile ( \
  91. _NOLIBC_SYSCALL_STACK_RESERVE \
  92. "syscall\n" \
  93. _NOLIBC_SYSCALL_STACK_UNRESERVE \
  94. : "=r"(_num), "=r"(_arg4) \
  95. : "0"(_num), \
  96. "r"(_arg1), "r"(_arg2) \
  97. : _NOLIBC_SYSCALL_CLOBBERLIST \
  98. ); \
  99. _arg4 ? -_num : _num; \
  100. })
  101. #define my_syscall3(num, arg1, arg2, arg3) \
  102. ({ \
  103. register long _num __asm__ ("v0") = (num); \
  104. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  105. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  106. register long _arg3 __asm__ ("a2") = (long)(arg3); \
  107. register long _arg4 __asm__ ("a3"); \
  108. \
  109. __asm__ volatile ( \
  110. _NOLIBC_SYSCALL_STACK_RESERVE \
  111. "syscall\n" \
  112. _NOLIBC_SYSCALL_STACK_UNRESERVE \
  113. : "=r"(_num), "=r"(_arg4) \
  114. : "0"(_num), \
  115. "r"(_arg1), "r"(_arg2), "r"(_arg3) \
  116. : _NOLIBC_SYSCALL_CLOBBERLIST \
  117. ); \
  118. _arg4 ? -_num : _num; \
  119. })
  120. #define my_syscall4(num, arg1, arg2, arg3, arg4) \
  121. ({ \
  122. register long _num __asm__ ("v0") = (num); \
  123. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  124. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  125. register long _arg3 __asm__ ("a2") = (long)(arg3); \
  126. register long _arg4 __asm__ ("a3") = (long)(arg4); \
  127. \
  128. __asm__ volatile ( \
  129. _NOLIBC_SYSCALL_STACK_RESERVE \
  130. "syscall\n" \
  131. _NOLIBC_SYSCALL_STACK_UNRESERVE \
  132. : "=r" (_num), "=r"(_arg4) \
  133. : "0"(_num), \
  134. "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \
  135. : _NOLIBC_SYSCALL_CLOBBERLIST \
  136. ); \
  137. _arg4 ? -_num : _num; \
  138. })
  139. #if defined(_ABIO32)
  140. #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
  141. ({ \
  142. register long _num __asm__ ("v0") = (num); \
  143. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  144. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  145. register long _arg3 __asm__ ("a2") = (long)(arg3); \
  146. register long _arg4 __asm__ ("a3") = (long)(arg4); \
  147. register long _arg5 = (long)(arg5); \
  148. \
  149. __asm__ volatile ( \
  150. _NOLIBC_SYSCALL_STACK_RESERVE \
  151. "sw %7, 16($sp)\n" \
  152. "syscall\n" \
  153. _NOLIBC_SYSCALL_STACK_UNRESERVE \
  154. : "=r" (_num), "=r"(_arg4) \
  155. : "0"(_num), \
  156. "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \
  157. : _NOLIBC_SYSCALL_CLOBBERLIST \
  158. ); \
  159. _arg4 ? -_num : _num; \
  160. })
  161. #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
  162. ({ \
  163. register long _num __asm__ ("v0") = (num); \
  164. register long _arg1 __asm__ ("a0") = (long)(arg1); \
  165. register long _arg2 __asm__ ("a1") = (long)(arg2); \
  166. register long _arg3 __asm__ ("a2") = (long)(arg3); \
  167. register long _arg4 __asm__ ("a3") = (long)(arg4); \
  168. register long _arg5 = (long)(arg5); \
  169. register long _arg6 = (long)(arg6); \
  170. \
  171. __asm__ volatile ( \
  172. _NOLIBC_SYSCALL_STACK_RESERVE \
  173. "sw %7, 16($sp)\n" \
  174. "sw %8, 20($sp)\n" \
  175. "syscall\n" \
  176. _NOLIBC_SYSCALL_STACK_UNRESERVE \
  177. : "=r" (_num), "=r"(_arg4) \
  178. : "0"(_num), \
  179. "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
  180. "r"(_arg6) \
  181. : _NOLIBC_SYSCALL_CLOBBERLIST \
  182. ); \
  183. _arg4 ? -_num : _num; \
  184. })
  185. #else /* _ABIN32 || _ABI64 */
  186. #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
  187. ({ \
  188. register long _num __asm__ ("v0") = (num); \
  189. register long _arg1 __asm__ ("$4") = (long)(arg1); \
  190. register long _arg2 __asm__ ("$5") = (long)(arg2); \
  191. register long _arg3 __asm__ ("$6") = (long)(arg3); \
  192. register long _arg4 __asm__ ("$7") = (long)(arg4); \
  193. register long _arg5 __asm__ ("$8") = (long)(arg5); \
  194. \
  195. __asm__ volatile ( \
  196. "syscall\n" \
  197. : "=r" (_num), "=r"(_arg4) \
  198. : "0"(_num), \
  199. "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \
  200. : _NOLIBC_SYSCALL_CLOBBERLIST \
  201. ); \
  202. _arg4 ? -_num : _num; \
  203. })
  204. #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
  205. ({ \
  206. register long _num __asm__ ("v0") = (num); \
  207. register long _arg1 __asm__ ("$4") = (long)(arg1); \
  208. register long _arg2 __asm__ ("$5") = (long)(arg2); \
  209. register long _arg3 __asm__ ("$6") = (long)(arg3); \
  210. register long _arg4 __asm__ ("$7") = (long)(arg4); \
  211. register long _arg5 __asm__ ("$8") = (long)(arg5); \
  212. register long _arg6 __asm__ ("$9") = (long)(arg6); \
  213. \
  214. __asm__ volatile ( \
  215. "syscall\n" \
  216. : "=r" (_num), "=r"(_arg4) \
  217. : "0"(_num), \
  218. "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
  219. "r"(_arg6) \
  220. : _NOLIBC_SYSCALL_CLOBBERLIST \
  221. ); \
  222. _arg4 ? -_num : _num; \
  223. })
  224. #endif /* _ABIO32 */
  225. #ifndef NOLIBC_NO_RUNTIME
  226. /* startup code, note that it's called __start on MIPS */
  227. void __start(void);
  228. void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector __start(void)
  229. {
  230. __asm__ volatile (
  231. "move $a0, $sp\n" /* save stack pointer to $a0, as arg1 of _start_c */
  232. #if defined(_ABIO32)
  233. "addiu $sp, $sp, -16\n" /* the callee expects to save a0..a3 there */
  234. #endif /* _ABIO32 */
  235. "lui $t9, %hi(_start_c)\n" /* ABI requires current function address in $t9 */
  236. "ori $t9, %lo(_start_c)\n"
  237. #if defined(_ABI64)
  238. "lui $t0, %highest(_start_c)\n"
  239. "ori $t0, %higher(_start_c)\n"
  240. "dsll $t0, 0x20\n"
  241. "or $t9, $t0\n"
  242. #endif /* _ABI64 */
  243. "jalr $t9\n" /* transfer to c runtime */
  244. );
  245. __nolibc_entrypoint_epilogue();
  246. }
  247. #endif /* NOLIBC_NO_RUNTIME */
  248. #endif /* _NOLIBC_ARCH_MIPS_H */