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 /* 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
33 #define _NOLIBC_SYSCALL_CLOBBERLIST \
34 "memory", "cc", "at", "v1", "hi", "lo", \
35 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"
36
37 #define my_syscall0(num) \
38 ({ \
39 register long _num __asm__ ("v0") = (num); \
40 register long _arg4 __asm__ ("a3"); \
41 \
42 __asm__ volatile ( \
43 "addiu $sp, $sp, -32\n" \
44 "syscall\n" \
45 "addiu $sp, $sp, 32\n" \
46 : "=r"(_num), "=r"(_arg4) \
47 : "r"(_num) \
48 : _NOLIBC_SYSCALL_CLOBBERLIST \
49 ); \
50 _arg4 ? -_num : _num; \
51 })
52
53 #define my_syscall1(num, arg1) \
54 ({ \
55 register long _num __asm__ ("v0") = (num); \
56 register long _arg1 __asm__ ("a0") = (long)(arg1); \
57 register long _arg4 __asm__ ("a3"); \
58 \
59 __asm__ volatile ( \
60 "addiu $sp, $sp, -32\n" \
61 "syscall\n" \
62 "addiu $sp, $sp, 32\n" \
63 : "=r"(_num), "=r"(_arg4) \
64 : "0"(_num), \
65 "r"(_arg1) \
66 : _NOLIBC_SYSCALL_CLOBBERLIST \
67 ); \
68 _arg4 ? -_num : _num; \
69 })
70
71 #define my_syscall2(num, arg1, arg2) \
72 ({ \
73 register long _num __asm__ ("v0") = (num); \
74 register long _arg1 __asm__ ("a0") = (long)(arg1); \
75 register long _arg2 __asm__ ("a1") = (long)(arg2); \
76 register long _arg4 __asm__ ("a3"); \
77 \
78 __asm__ volatile ( \
79 "addiu $sp, $sp, -32\n" \
80 "syscall\n" \
81 "addiu $sp, $sp, 32\n" \
82 : "=r"(_num), "=r"(_arg4) \
83 : "0"(_num), \
84 "r"(_arg1), "r"(_arg2) \
85 : _NOLIBC_SYSCALL_CLOBBERLIST \
86 ); \
87 _arg4 ? -_num : _num; \
88 })
89
90 #define my_syscall3(num, arg1, arg2, arg3) \
91 ({ \
92 register long _num __asm__ ("v0") = (num); \
93 register long _arg1 __asm__ ("a0") = (long)(arg1); \
94 register long _arg2 __asm__ ("a1") = (long)(arg2); \
95 register long _arg3 __asm__ ("a2") = (long)(arg3); \
96 register long _arg4 __asm__ ("a3"); \
97 \
98 __asm__ volatile ( \
99 "addiu $sp, $sp, -32\n" \
100 "syscall\n" \
101 "addiu $sp, $sp, 32\n" \
102 : "=r"(_num), "=r"(_arg4) \
103 : "0"(_num), \
104 "r"(_arg1), "r"(_arg2), "r"(_arg3) \
105 : _NOLIBC_SYSCALL_CLOBBERLIST \
106 ); \
107 _arg4 ? -_num : _num; \
108 })
109
110 #define my_syscall4(num, arg1, arg2, arg3, arg4) \
111 ({ \
112 register long _num __asm__ ("v0") = (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 \
118 __asm__ volatile ( \
119 "addiu $sp, $sp, -32\n" \
120 "syscall\n" \
121 "addiu $sp, $sp, 32\n" \
122 : "=r" (_num), "=r"(_arg4) \
123 : "0"(_num), \
124 "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \
125 : _NOLIBC_SYSCALL_CLOBBERLIST \
126 ); \
127 _arg4 ? -_num : _num; \
128 })
129
130 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
131 ({ \
132 register long _num __asm__ ("v0") = (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 = (long)(arg5); \
138 \
139 __asm__ volatile ( \
140 "addiu $sp, $sp, -32\n" \
141 "sw %7, 16($sp)\n" \
142 "syscall\n" \
143 "addiu $sp, $sp, 32\n" \
144 : "=r" (_num), "=r"(_arg4) \
145 : "0"(_num), \
146 "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \
147 : _NOLIBC_SYSCALL_CLOBBERLIST \
148 ); \
149 _arg4 ? -_num : _num; \
150 })
151
152 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
153 ({ \
154 register long _num __asm__ ("v0") = (num); \
155 register long _arg1 __asm__ ("a0") = (long)(arg1); \
156 register long _arg2 __asm__ ("a1") = (long)(arg2); \
157 register long _arg3 __asm__ ("a2") = (long)(arg3); \
158 register long _arg4 __asm__ ("a3") = (long)(arg4); \
159 register long _arg5 = (long)(arg5); \
160 register long _arg6 = (long)(arg6); \
161 \
162 __asm__ volatile ( \
163 "addiu $sp, $sp, -32\n" \
164 "sw %7, 16($sp)\n" \
165 "sw %8, 20($sp)\n" \
166 "syscall\n" \
167 "addiu $sp, $sp, 32\n" \
168 : "=r" (_num), "=r"(_arg4) \
169 : "0"(_num), \
170 "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
171 "r"(_arg6) \
172 : _NOLIBC_SYSCALL_CLOBBERLIST \
173 ); \
174 _arg4 ? -_num : _num; \
175 })
176
177 /* startup code, note that it's called __start on MIPS */
__start(void)178 void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector __start(void)
179 {
180 __asm__ volatile (
181 ".set push\n"
182 ".set noreorder\n"
183 ".option pic0\n"
184 "move $a0, $sp\n" /* save stack pointer to $a0, as arg1 of _start_c */
185 "li $t0, -8\n"
186 "and $sp, $sp, $t0\n" /* $sp must be 8-byte aligned */
187 "addiu $sp, $sp, -16\n" /* the callee expects to save a0..a3 there */
188 "jal _start_c\n" /* transfer to c runtime */
189 " nop\n" /* delayed slot */
190 ".set pop\n"
191 );
192 __builtin_unreachable();
193 }
194
195 #endif /* _NOLIBC_ARCH_MIPS_H */
196