Warning: This file is not a C or C++ file. It does not have highlighting.
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 */ |
182 | void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __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 | "jal _start_c\n" /* transfer to c runtime */ |
198 | " nop\n" /* delayed slot */ |
199 | ".set pop\n" |
200 | ); |
201 | __builtin_unreachable(); |
202 | } |
203 | |
204 | #endif /* _NOLIBC_ARCH_MIPS_H */ |
205 |
Warning: This file is not a C or C++ file. It does not have highlighting.