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