xref: /openbmc/linux/tools/include/nolibc/arch-mips.h (revision 2a598d0b)
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 
12 /* The struct returned by the stat() syscall. 88 bytes are returned by the
13  * syscall.
14  */
15 struct sys_stat_struct {
16 	unsigned int  st_dev;
17 	long          st_pad1[3];
18 	unsigned long st_ino;
19 	unsigned int  st_mode;
20 	unsigned int  st_nlink;
21 	unsigned int  st_uid;
22 	unsigned int  st_gid;
23 	unsigned int  st_rdev;
24 	long          st_pad2[2];
25 	long          st_size;
26 	long          st_pad3;
27 
28 	long          st_atime;
29 	long          st_atime_nsec;
30 	long          st_mtime;
31 	long          st_mtime_nsec;
32 
33 	long          st_ctime;
34 	long          st_ctime_nsec;
35 	long          st_blksize;
36 	long          st_blocks;
37 	long          st_pad4[14];
38 };
39 
40 /* Syscalls for MIPS ABI O32 :
41  *   - WARNING! there's always a delayed slot!
42  *   - WARNING again, the syntax is different, registers take a '$' and numbers
43  *     do not.
44  *   - registers are 32-bit
45  *   - stack is 8-byte aligned
46  *   - syscall number is passed in v0 (starts at 0xfa0).
47  *   - arguments are in a0, a1, a2, a3, then the stack. The caller needs to
48  *     leave some room in the stack for the callee to save a0..a3 if needed.
49  *   - Many registers are clobbered, in fact only a0..a2 and s0..s8 are
50  *     preserved. See: https://www.linux-mips.org/wiki/Syscall as well as
51  *     scall32-o32.S in the kernel sources.
52  *   - the system call is performed by calling "syscall"
53  *   - syscall return comes in v0, and register a3 needs to be checked to know
54  *     if an error occurred, in which case errno is in v0.
55  *   - the arguments are cast to long and assigned into the target registers
56  *     which are then simply passed as registers to the asm code, so that we
57  *     don't have to experience issues with register constraints.
58  */
59 
60 #define my_syscall0(num)                                                      \
61 ({                                                                            \
62 	register long _num __asm__ ("v0") = (num);                            \
63 	register long _arg4 __asm__ ("a3");                                   \
64 	                                                                      \
65 	__asm__  volatile (                                                   \
66 		"addiu $sp, $sp, -32\n"                                       \
67 		"syscall\n"                                                   \
68 		"addiu $sp, $sp, 32\n"                                        \
69 		: "=r"(_num), "=r"(_arg4)                                     \
70 		: "r"(_num)                                                   \
71 		: "memory", "cc", "at", "v1", "hi", "lo",                     \
72 	          "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
73 	);                                                                    \
74 	_arg4 ? -_num : _num;                                                 \
75 })
76 
77 #define my_syscall1(num, arg1)                                                \
78 ({                                                                            \
79 	register long _num __asm__ ("v0") = (num);                            \
80 	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
81 	register long _arg4 __asm__ ("a3");                                   \
82 	                                                                      \
83 	__asm__  volatile (                                                   \
84 		"addiu $sp, $sp, -32\n"                                       \
85 		"syscall\n"                                                   \
86 		"addiu $sp, $sp, 32\n"                                        \
87 		: "=r"(_num), "=r"(_arg4)                                     \
88 		: "0"(_num),                                                  \
89 		  "r"(_arg1)                                                  \
90 		: "memory", "cc", "at", "v1", "hi", "lo",                     \
91 	          "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
92 	);                                                                    \
93 	_arg4 ? -_num : _num;                                                 \
94 })
95 
96 #define my_syscall2(num, arg1, arg2)                                          \
97 ({                                                                            \
98 	register long _num __asm__ ("v0") = (num);                            \
99 	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
100 	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
101 	register long _arg4 __asm__ ("a3");                                   \
102 	                                                                      \
103 	__asm__  volatile (                                                   \
104 		"addiu $sp, $sp, -32\n"                                       \
105 		"syscall\n"                                                   \
106 		"addiu $sp, $sp, 32\n"                                        \
107 		: "=r"(_num), "=r"(_arg4)                                     \
108 		: "0"(_num),                                                  \
109 		  "r"(_arg1), "r"(_arg2)                                      \
110 		: "memory", "cc", "at", "v1", "hi", "lo",                     \
111 	          "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
112 	);                                                                    \
113 	_arg4 ? -_num : _num;                                                 \
114 })
115 
116 #define my_syscall3(num, arg1, arg2, arg3)                                    \
117 ({                                                                            \
118 	register long _num __asm__ ("v0")  = (num);                           \
119 	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
120 	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
121 	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
122 	register long _arg4 __asm__ ("a3");                                   \
123 	                                                                      \
124 	__asm__  volatile (                                                   \
125 		"addiu $sp, $sp, -32\n"                                       \
126 		"syscall\n"                                                   \
127 		"addiu $sp, $sp, 32\n"                                        \
128 		: "=r"(_num), "=r"(_arg4)                                     \
129 		: "0"(_num),                                                  \
130 		  "r"(_arg1), "r"(_arg2), "r"(_arg3)                          \
131 		: "memory", "cc", "at", "v1", "hi", "lo",                     \
132 	          "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
133 	);                                                                    \
134 	_arg4 ? -_num : _num;                                                 \
135 })
136 
137 #define my_syscall4(num, arg1, arg2, arg3, arg4)                              \
138 ({                                                                            \
139 	register long _num __asm__ ("v0") = (num);                            \
140 	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
141 	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
142 	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
143 	register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
144 	                                                                      \
145 	__asm__  volatile (                                                   \
146 		"addiu $sp, $sp, -32\n"                                       \
147 		"syscall\n"                                                   \
148 		"addiu $sp, $sp, 32\n"                                        \
149 		: "=r" (_num), "=r"(_arg4)                                    \
150 		: "0"(_num),                                                  \
151 		  "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4)              \
152 		: "memory", "cc", "at", "v1", "hi", "lo",                     \
153 	          "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
154 	);                                                                    \
155 	_arg4 ? -_num : _num;                                                 \
156 })
157 
158 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5)                        \
159 ({                                                                            \
160 	register long _num __asm__ ("v0") = (num);                            \
161 	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
162 	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
163 	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
164 	register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
165 	register long _arg5 = (long)(arg5);                                   \
166 	                                                                      \
167 	__asm__  volatile (                                                   \
168 		"addiu $sp, $sp, -32\n"                                       \
169 		"sw %7, 16($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 		: "memory", "cc", "at", "v1", "hi", "lo",                     \
176 	          "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
177 	);                                                                    \
178 	_arg4 ? -_num : _num;                                                 \
179 })
180 
181 char **environ __attribute__((weak));
182 const unsigned long *_auxv __attribute__((weak));
183 
184 /* startup code, note that it's called __start on MIPS */
185 void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector __start(void)
186 {
187 	__asm__ volatile (
188 		/*".set nomips16\n"*/
189 		".set push\n"
190 		".set    noreorder\n"
191 		".option pic0\n"
192 #ifdef _NOLIBC_STACKPROTECTOR
193 		"jal __stack_chk_init\n" /* initialize stack protector                         */
194 		"nop\n"                  /* delayed slot                                       */
195 #endif
196 		/*".ent __start\n"*/
197 		/*"__start:\n"*/
198 		"lw $a0,($sp)\n"        /* argc was in the stack                               */
199 		"addiu  $a1, $sp, 4\n"  /* argv = sp + 4                                       */
200 		"sll $a2, $a0, 2\n"     /* a2 = argc * 4                                       */
201 		"add   $a2, $a2, $a1\n" /* envp = argv + 4*argc ...                            */
202 		"addiu $a2, $a2, 4\n"   /*        ... + 4                                      */
203 		"lui $a3, %hi(environ)\n"     /* load environ into a3 (hi)                     */
204 		"addiu $a3, %lo(environ)\n"   /* load environ into a3 (lo)                     */
205 		"sw $a2,($a3)\n"              /* store envp(a2) into environ                   */
206 
207 		"move $t0, $a2\n"             /* iterate t0 over envp, look for NULL           */
208 		"0:"                          /* do {                                          */
209 		"lw $a3, ($t0)\n"             /*   a3=*(t0);                                   */
210 		"bne $a3, $0, 0b\n"           /* } while (a3);                                 */
211 		"addiu $t0, $t0, 4\n"         /* delayed slot: t0+=4;                          */
212 		"lui $a3, %hi(_auxv)\n"       /* load _auxv into a3 (hi)                       */
213 		"addiu $a3, %lo(_auxv)\n"     /* load _auxv into a3 (lo)                       */
214 		"sw $t0, ($a3)\n"             /* store t0 into _auxv                           */
215 
216 		"li $t0, -8\n"
217 		"and $sp, $sp, $t0\n"   /* sp must be 8-byte aligned                           */
218 		"addiu $sp,$sp,-16\n"   /* the callee expects to save a0..a3 there!            */
219 		"jal main\n"            /* main() returns the status code, we'll exit with it. */
220 		"nop\n"                 /* delayed slot                                        */
221 		"move $a0, $v0\n"       /* retrieve 32-bit exit code from v0                   */
222 		"li $v0, 4001\n"        /* NR_exit == 4001                                     */
223 		"syscall\n"
224 		/*".end __start\n"*/
225 		".set pop\n"
226 	);
227 	__builtin_unreachable();
228 }
229 
230 #endif /* _NOLIBC_ARCH_MIPS_H */
231