1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3  * AARCH64 specific definitions for NOLIBC
4  * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
5  */
6 
7 #ifndef _NOLIBC_ARCH_AARCH64_H
8 #define _NOLIBC_ARCH_AARCH64_H
9 
10 /* The struct returned by the newfstatat() syscall. Differs slightly from the
11  * x86_64's stat one by field ordering, so be careful.
12  */
13 struct sys_stat_struct {
14 	unsigned long   st_dev;
15 	unsigned long   st_ino;
16 	unsigned int    st_mode;
17 	unsigned int    st_nlink;
18 	unsigned int    st_uid;
19 	unsigned int    st_gid;
20 
21 	unsigned long   st_rdev;
22 	unsigned long   __pad1;
23 	long            st_size;
24 	int             st_blksize;
25 	int             __pad2;
26 
27 	long            st_blocks;
28 	long            st_atime;
29 	unsigned long   st_atime_nsec;
30 	long            st_mtime;
31 
32 	unsigned long   st_mtime_nsec;
33 	long            st_ctime;
34 	unsigned long   st_ctime_nsec;
35 	unsigned int    __unused[2];
36 };
37 
38 /* Syscalls for AARCH64 :
39  *   - registers are 64-bit
40  *   - stack is 16-byte aligned
41  *   - syscall number is passed in x8
42  *   - arguments are in x0, x1, x2, x3, x4, x5
43  *   - the system call is performed by calling svc 0
44  *   - syscall return comes in x0.
45  *   - the arguments are cast to long and assigned into the target registers
46  *     which are then simply passed as registers to the asm code, so that we
47  *     don't have to experience issues with register constraints.
48  *
49  * On aarch64, select() is not implemented so we have to use pselect6().
50  */
51 #define __ARCH_WANT_SYS_PSELECT6
52 
53 #define my_syscall0(num)                                                      \
54 ({                                                                            \
55 	register long _num  __asm__ ("x8") = (num);                           \
56 	register long _arg1 __asm__ ("x0");                                   \
57 	                                                                      \
58 	__asm__  volatile (                                                   \
59 		"svc #0\n"                                                    \
60 		: "=r"(_arg1)                                                 \
61 		: "r"(_num)                                                   \
62 		: "memory", "cc"                                              \
63 	);                                                                    \
64 	_arg1;                                                                \
65 })
66 
67 #define my_syscall1(num, arg1)                                                \
68 ({                                                                            \
69 	register long _num  __asm__ ("x8") = (num);                           \
70 	register long _arg1 __asm__ ("x0") = (long)(arg1);                    \
71 	                                                                      \
72 	__asm__  volatile (                                                   \
73 		"svc #0\n"                                                    \
74 		: "=r"(_arg1)                                                 \
75 		: "r"(_arg1),                                                 \
76 		  "r"(_num)                                                   \
77 		: "memory", "cc"                                              \
78 	);                                                                    \
79 	_arg1;                                                                \
80 })
81 
82 #define my_syscall2(num, arg1, arg2)                                          \
83 ({                                                                            \
84 	register long _num  __asm__ ("x8") = (num);                           \
85 	register long _arg1 __asm__ ("x0") = (long)(arg1);                    \
86 	register long _arg2 __asm__ ("x1") = (long)(arg2);                    \
87 	                                                                      \
88 	__asm__  volatile (                                                   \
89 		"svc #0\n"                                                    \
90 		: "=r"(_arg1)                                                 \
91 		: "r"(_arg1), "r"(_arg2),                                     \
92 		  "r"(_num)                                                   \
93 		: "memory", "cc"                                              \
94 	);                                                                    \
95 	_arg1;                                                                \
96 })
97 
98 #define my_syscall3(num, arg1, arg2, arg3)                                    \
99 ({                                                                            \
100 	register long _num  __asm__ ("x8") = (num);                           \
101 	register long _arg1 __asm__ ("x0") = (long)(arg1);                    \
102 	register long _arg2 __asm__ ("x1") = (long)(arg2);                    \
103 	register long _arg3 __asm__ ("x2") = (long)(arg3);                    \
104 	                                                                      \
105 	__asm__  volatile (                                                   \
106 		"svc #0\n"                                                    \
107 		: "=r"(_arg1)                                                 \
108 		: "r"(_arg1), "r"(_arg2), "r"(_arg3),                         \
109 		  "r"(_num)                                                   \
110 		: "memory", "cc"                                              \
111 	);                                                                    \
112 	_arg1;                                                                \
113 })
114 
115 #define my_syscall4(num, arg1, arg2, arg3, arg4)                              \
116 ({                                                                            \
117 	register long _num  __asm__ ("x8") = (num);                           \
118 	register long _arg1 __asm__ ("x0") = (long)(arg1);                    \
119 	register long _arg2 __asm__ ("x1") = (long)(arg2);                    \
120 	register long _arg3 __asm__ ("x2") = (long)(arg3);                    \
121 	register long _arg4 __asm__ ("x3") = (long)(arg4);                    \
122 	                                                                      \
123 	__asm__  volatile (                                                   \
124 		"svc #0\n"                                                    \
125 		: "=r"(_arg1)                                                 \
126 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4),             \
127 		  "r"(_num)                                                   \
128 		: "memory", "cc"                                              \
129 	);                                                                    \
130 	_arg1;                                                                \
131 })
132 
133 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5)                        \
134 ({                                                                            \
135 	register long _num  __asm__ ("x8") = (num);                           \
136 	register long _arg1 __asm__ ("x0") = (long)(arg1);                    \
137 	register long _arg2 __asm__ ("x1") = (long)(arg2);                    \
138 	register long _arg3 __asm__ ("x2") = (long)(arg3);                    \
139 	register long _arg4 __asm__ ("x3") = (long)(arg4);                    \
140 	register long _arg5 __asm__ ("x4") = (long)(arg5);                    \
141 	                                                                      \
142 	__asm__  volatile (                                                   \
143 		"svc #0\n"                                                    \
144 		: "=r" (_arg1)                                                \
145 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
146 		  "r"(_num)                                                   \
147 		: "memory", "cc"                                              \
148 	);                                                                    \
149 	_arg1;                                                                \
150 })
151 
152 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)                  \
153 ({                                                                            \
154 	register long _num  __asm__ ("x8") = (num);                           \
155 	register long _arg1 __asm__ ("x0") = (long)(arg1);                    \
156 	register long _arg2 __asm__ ("x1") = (long)(arg2);                    \
157 	register long _arg3 __asm__ ("x2") = (long)(arg3);                    \
158 	register long _arg4 __asm__ ("x3") = (long)(arg4);                    \
159 	register long _arg5 __asm__ ("x4") = (long)(arg5);                    \
160 	register long _arg6 __asm__ ("x5") = (long)(arg6);                    \
161 	                                                                      \
162 	__asm__  volatile (                                                   \
163 		"svc #0\n"                                                    \
164 		: "=r" (_arg1)                                                \
165 		: "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
166 		  "r"(_arg6), "r"(_num)                                       \
167 		: "memory", "cc"                                              \
168 	);                                                                    \
169 	_arg1;                                                                \
170 })
171 
172 char **environ __attribute__((weak));
173 const unsigned long *_auxv __attribute__((weak));
174 
175 /* startup code */
176 void __attribute__((weak,noreturn,optimize("omit-frame-pointer"))) _start(void)
177 {
178 	__asm__ volatile (
179 		"ldr x0, [sp]\n"     // argc (x0) was in the stack
180 		"add x1, sp, 8\n"    // argv (x1) = sp
181 		"lsl x2, x0, 3\n"    // envp (x2) = 8*argc ...
182 		"add x2, x2, 8\n"    //           + 8 (skip null)
183 		"add x2, x2, x1\n"   //           + argv
184 		"adrp x3, environ\n"          // x3 = &environ (high bits)
185 		"str x2, [x3, #:lo12:environ]\n" // store envp into environ
186 		"mov x4, x2\n"       // search for auxv (follows NULL after last env)
187 		"0:\n"
188 		"ldr x5, [x4], 8\n"  // x5 = *x4; x4 += 8
189 		"cbnz x5, 0b\n"      // and stop at NULL after last env
190 		"adrp x3, _auxv\n"   // x3 = &_auxv (high bits)
191 		"str x4, [x3, #:lo12:_auxv]\n" // store x4 into _auxv
192 		"and sp, x1, -16\n"  // sp must be 16-byte aligned in the callee
193 		"bl main\n"          // main() returns the status code, we'll exit with it.
194 		"mov x8, 93\n"       // NR_exit == 93
195 		"svc #0\n"
196 	);
197 	__builtin_unreachable();
198 }
199 #endif // _NOLIBC_ARCH_AARCH64_H
200