xref: /openbmc/linux/tools/include/nolibc/arch-arm.h (revision c4c3c32d)
1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3  * ARM specific definitions for NOLIBC
4  * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
5  */
6 
7 #ifndef _NOLIBC_ARCH_ARM_H
8 #define _NOLIBC_ARCH_ARM_H
9 
10 #include "compiler.h"
11 
12 /* The struct returned by the stat() syscall, 32-bit only, the syscall returns
13  * exactly 56 bytes (stops before the unused array). In big endian, the format
14  * differs as devices are returned as short only.
15  */
16 struct sys_stat_struct {
17 #if defined(__ARMEB__)
18 	unsigned short st_dev;
19 	unsigned short __pad1;
20 #else
21 	unsigned long  st_dev;
22 #endif
23 	unsigned long  st_ino;
24 	unsigned short st_mode;
25 	unsigned short st_nlink;
26 	unsigned short st_uid;
27 	unsigned short st_gid;
28 
29 #if defined(__ARMEB__)
30 	unsigned short st_rdev;
31 	unsigned short __pad2;
32 #else
33 	unsigned long  st_rdev;
34 #endif
35 	unsigned long  st_size;
36 	unsigned long  st_blksize;
37 	unsigned long  st_blocks;
38 
39 	unsigned long  st_atime;
40 	unsigned long  st_atime_nsec;
41 	unsigned long  st_mtime;
42 	unsigned long  st_mtime_nsec;
43 
44 	unsigned long  st_ctime;
45 	unsigned long  st_ctime_nsec;
46 	unsigned long  __unused[2];
47 };
48 
49 /* Syscalls for ARM in ARM or Thumb modes :
50  *   - registers are 32-bit
51  *   - stack is 8-byte aligned
52  *     ( http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html)
53  *   - syscall number is passed in r7
54  *   - arguments are in r0, r1, r2, r3, r4, r5
55  *   - the system call is performed by calling svc #0
56  *   - syscall return comes in r0.
57  *   - only lr is clobbered.
58  *   - the arguments are cast to long and assigned into the target registers
59  *     which are then simply passed as registers to the asm code, so that we
60  *     don't have to experience issues with register constraints.
61  *   - the syscall number is always specified last in order to allow to force
62  *     some registers before (gcc refuses a %-register at the last position).
63  *   - in thumb mode without -fomit-frame-pointer, r7 is also used to store the
64  *     frame pointer, and we cannot directly assign it as a register variable,
65  *     nor can we clobber it. Instead we assign the r6 register and swap it
66  *     with r7 before calling svc, and r6 is marked as clobbered.
67  *     We're just using any regular register which we assign to r7 after saving
68  *     it.
69  *
70  * Also, ARM supports the old_select syscall if newselect is not available
71  */
72 #define __ARCH_WANT_SYS_OLD_SELECT
73 
74 #if (defined(__THUMBEB__) || defined(__THUMBEL__)) && \
75     !defined(NOLIBC_OMIT_FRAME_POINTER)
76 /* swap r6,r7 needed in Thumb mode since we can't use nor clobber r7 */
77 #define _NOLIBC_SYSCALL_REG         "r6"
78 #define _NOLIBC_THUMB_SET_R7        "eor r7, r6\neor r6, r7\neor r7, r6\n"
79 #define _NOLIBC_THUMB_RESTORE_R7    "mov r7, r6\n"
80 
81 #else  /* we're in ARM mode */
82 /* in Arm mode we can directly use r7 */
83 #define _NOLIBC_SYSCALL_REG         "r7"
84 #define _NOLIBC_THUMB_SET_R7        ""
85 #define _NOLIBC_THUMB_RESTORE_R7    ""
86 
87 #endif /* end THUMB */
88 
89 #define my_syscall0(num)                                                      \
90 ({                                                                            \
91 	register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
92 	register long _arg1 __asm__ ("r0");                                   \
93 	                                                                      \
94 	__asm__  volatile (                                                   \
95 		_NOLIBC_THUMB_SET_R7                                          \
96 		"svc #0\n"                                                    \
97 		_NOLIBC_THUMB_RESTORE_R7                                      \
98 		: "=r"(_arg1), "=r"(_num)                                     \
99 		: "r"(_arg1),                                                 \
100 		  "r"(_num)                                                   \
101 		: "memory", "cc", "lr"                                        \
102 	);                                                                    \
103 	_arg1;                                                                \
104 })
105 
106 #define my_syscall1(num, arg1)                                                \
107 ({                                                                            \
108 	register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
109 	register long _arg1 __asm__ ("r0") = (long)(arg1);                    \
110 	                                                                      \
111 	__asm__  volatile (                                                   \
112 		_NOLIBC_THUMB_SET_R7                                          \
113 		"svc #0\n"                                                    \
114 		_NOLIBC_THUMB_RESTORE_R7                                      \
115 		: "=r"(_arg1), "=r" (_num)                                    \
116 		: "r"(_arg1),                                                 \
117 		  "r"(_num)                                                   \
118 		: "memory", "cc", "lr"                                        \
119 	);                                                                    \
120 	_arg1;                                                                \
121 })
122 
123 #define my_syscall2(num, arg1, arg2)                                          \
124 ({                                                                            \
125 	register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
126 	register long _arg1 __asm__ ("r0") = (long)(arg1);                    \
127 	register long _arg2 __asm__ ("r1") = (long)(arg2);                    \
128 	                                                                      \
129 	__asm__  volatile (                                                   \
130 		_NOLIBC_THUMB_SET_R7                                          \
131 		"svc #0\n"                                                    \
132 		_NOLIBC_THUMB_RESTORE_R7                                      \
133 		: "=r"(_arg1), "=r" (_num)                                    \
134 		: "r"(_arg1), "r"(_arg2),                                     \
135 		  "r"(_num)                                                   \
136 		: "memory", "cc", "lr"                                        \
137 	);                                                                    \
138 	_arg1;                                                                \
139 })
140 
141 #define my_syscall3(num, arg1, arg2, arg3)                                    \
142 ({                                                                            \
143 	register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
144 	register long _arg1 __asm__ ("r0") = (long)(arg1);                    \
145 	register long _arg2 __asm__ ("r1") = (long)(arg2);                    \
146 	register long _arg3 __asm__ ("r2") = (long)(arg3);                    \
147 	                                                                      \
148 	__asm__  volatile (                                                   \
149 		_NOLIBC_THUMB_SET_R7                                          \
150 		"svc #0\n"                                                    \
151 		_NOLIBC_THUMB_RESTORE_R7                                      \
152 		: "=r"(_arg1), "=r" (_num)                                    \
153 		: "r"(_arg1), "r"(_arg2), "r"(_arg3),                         \
154 		  "r"(_num)                                                   \
155 		: "memory", "cc", "lr"                                        \
156 	);                                                                    \
157 	_arg1;                                                                \
158 })
159 
160 #define my_syscall4(num, arg1, arg2, arg3, arg4)                              \
161 ({                                                                            \
162 	register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
163 	register long _arg1 __asm__ ("r0") = (long)(arg1);                    \
164 	register long _arg2 __asm__ ("r1") = (long)(arg2);                    \
165 	register long _arg3 __asm__ ("r2") = (long)(arg3);                    \
166 	register long _arg4 __asm__ ("r3") = (long)(arg4);                    \
167 	                                                                      \
168 	__asm__  volatile (                                                   \
169 		_NOLIBC_THUMB_SET_R7                                          \
170 		"svc #0\n"                                                    \
171 		_NOLIBC_THUMB_RESTORE_R7                                      \
172 		: "=r"(_arg1), "=r" (_num)                                    \
173 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4),             \
174 		  "r"(_num)                                                   \
175 		: "memory", "cc", "lr"                                        \
176 	);                                                                    \
177 	_arg1;                                                                \
178 })
179 
180 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5)                        \
181 ({                                                                            \
182 	register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
183 	register long _arg1 __asm__ ("r0") = (long)(arg1);                    \
184 	register long _arg2 __asm__ ("r1") = (long)(arg2);                    \
185 	register long _arg3 __asm__ ("r2") = (long)(arg3);                    \
186 	register long _arg4 __asm__ ("r3") = (long)(arg4);                    \
187 	register long _arg5 __asm__ ("r4") = (long)(arg5);                    \
188 	                                                                      \
189 	__asm__  volatile (                                                   \
190 		_NOLIBC_THUMB_SET_R7                                          \
191 		"svc #0\n"                                                    \
192 		_NOLIBC_THUMB_RESTORE_R7                                      \
193 		: "=r"(_arg1), "=r" (_num)                                    \
194 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
195 		  "r"(_num)                                                   \
196 		: "memory", "cc", "lr"                                        \
197 	);                                                                    \
198 	_arg1;                                                                \
199 })
200 
201 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)                  \
202 ({                                                                            \
203 	register long _num  __asm__(_NOLIBC_SYSCALL_REG) = (num);             \
204 	register long _arg1 __asm__ ("r0") = (long)(arg1);                    \
205 	register long _arg2 __asm__ ("r1") = (long)(arg2);                    \
206 	register long _arg3 __asm__ ("r2") = (long)(arg3);                    \
207 	register long _arg4 __asm__ ("r3") = (long)(arg4);                    \
208 	register long _arg5 __asm__ ("r4") = (long)(arg5);                    \
209 	register long _arg6 __asm__ ("r5") = (long)(arg6);                    \
210 	                                                                      \
211 	__asm__  volatile (                                                   \
212 		_NOLIBC_THUMB_SET_R7                                          \
213 		"svc #0\n"                                                    \
214 		_NOLIBC_THUMB_RESTORE_R7                                      \
215 		: "=r"(_arg1), "=r" (_num)                                    \
216 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
217 		  "r"(_arg6), "r"(_num)                                       \
218 		: "memory", "cc", "lr"                                        \
219 	);                                                                    \
220 	_arg1;                                                                \
221 })
222 
223 
224 char **environ __attribute__((weak));
225 const unsigned long *_auxv __attribute__((weak));
226 
227 /* startup code */
228 void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) __no_stack_protector _start(void)
229 {
230 	__asm__ volatile (
231 #ifdef _NOLIBC_STACKPROTECTOR
232 		"bl __stack_chk_init\n"       /* initialize stack protector                          */
233 #endif
234 		"pop {%r0}\n"                 /* argc was in the stack                               */
235 		"mov %r1, %sp\n"              /* argv = sp                                           */
236 
237 		"add %r2, %r0, $1\n"          /* envp = (argc + 1) ...                               */
238 		"lsl %r2, %r2, $2\n"          /*        * 4        ...                               */
239 		"add %r2, %r2, %r1\n"         /*        + argv                                       */
240 		"ldr %r3, 1f\n"               /* r3 = &environ (see below)                           */
241 		"str %r2, [r3]\n"             /* store envp into environ                             */
242 
243 		"mov r4, r2\n"                /* search for auxv (follows NULL after last env)       */
244 		"0:\n"
245 		"mov r5, r4\n"                /* r5 = r4                                             */
246 		"add r4, r4, #4\n"            /* r4 += 4                                             */
247 		"ldr r5,[r5]\n"               /* r5 = *r5 = *(r4-4)                                  */
248 		"cmp r5, #0\n"                /* and stop at NULL after last env                     */
249 		"bne 0b\n"
250 		"ldr %r3, 2f\n"               /* r3 = &_auxv (low bits)                              */
251 		"str r4, [r3]\n"              /* store r4 into _auxv                                 */
252 
253 		"mov %r3, $8\n"               /* AAPCS : sp must be 8-byte aligned in the            */
254 		"neg %r3, %r3\n"              /*         callee, and bl doesn't push (lr=pc)         */
255 		"and %r3, %r3, %r1\n"         /* so we do sp = r1(=sp) & r3(=-8);                    */
256 		"mov %sp, %r3\n"
257 
258 		"bl main\n"                   /* main() returns the status code, we'll exit with it. */
259 		"movs r7, $1\n"               /* NR_exit == 1                                        */
260 		"svc $0x00\n"
261 		".align 2\n"                  /* below are the pointers to a few variables           */
262 		"1:\n"
263 		".word environ\n"
264 		"2:\n"
265 		".word _auxv\n"
266 	);
267 	__builtin_unreachable();
268 }
269 
270 #endif /* _NOLIBC_ARCH_ARM_H */
271