1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3  * LoongArch specific definitions for NOLIBC
4  * Copyright (C) 2023 Loongson Technology Corporation Limited
5  */
6 
7 #ifndef _NOLIBC_ARCH_LOONGARCH_H
8 #define _NOLIBC_ARCH_LOONGARCH_H
9 
10 #include "compiler.h"
11 #include "crt.h"
12 
13 /* Syscalls for LoongArch :
14  *   - stack is 16-byte aligned
15  *   - syscall number is passed in a7
16  *   - arguments are in a0, a1, a2, a3, a4, a5
17  *   - the system call is performed by calling "syscall 0"
18  *   - syscall return comes in a0
19  *   - the arguments are cast to long and assigned into the target
20  *     registers which are then simply passed as registers to the asm code,
21  *     so that we don't have to experience issues with register constraints.
22  *
23  * On LoongArch, select() is not implemented so we have to use pselect6().
24  */
25 #define __ARCH_WANT_SYS_PSELECT6
26 #define _NOLIBC_SYSCALL_CLOBBERLIST \
27 	"memory", "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", "$t8"
28 
29 #define my_syscall0(num)                                                      \
30 ({                                                                            \
31 	register long _num  __asm__ ("a7") = (num);                           \
32 	register long _arg1 __asm__ ("a0");                                   \
33 									      \
34 	__asm__ volatile (                                                    \
35 		"syscall 0\n"                                                 \
36 		: "=r"(_arg1)                                                 \
37 		: "r"(_num)                                                   \
38 		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
39 	);                                                                    \
40 	_arg1;                                                                \
41 })
42 
43 #define my_syscall1(num, arg1)                                                \
44 ({                                                                            \
45 	register long _num  __asm__ ("a7") = (num);                           \
46 	register long _arg1 __asm__ ("a0") = (long)(arg1);		      \
47 									      \
48 	__asm__ volatile (                                                    \
49 		"syscall 0\n"                                                 \
50 		: "+r"(_arg1)                                                 \
51 		: "r"(_num)                                                   \
52 		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
53 	);                                                                    \
54 	_arg1;                                                                \
55 })
56 
57 #define my_syscall2(num, arg1, arg2)                                          \
58 ({                                                                            \
59 	register long _num  __asm__ ("a7") = (num);                           \
60 	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
61 	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
62 									      \
63 	__asm__ volatile (                                                    \
64 		"syscall 0\n"                                                 \
65 		: "+r"(_arg1)                                                 \
66 		: "r"(_arg2),                                                 \
67 		  "r"(_num)                                                   \
68 		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
69 	);                                                                    \
70 	_arg1;                                                                \
71 })
72 
73 #define my_syscall3(num, arg1, arg2, arg3)                                    \
74 ({                                                                            \
75 	register long _num  __asm__ ("a7") = (num);                           \
76 	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
77 	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
78 	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
79 									      \
80 	__asm__ volatile (                                                    \
81 		"syscall 0\n"                                                 \
82 		: "+r"(_arg1)                                                 \
83 		: "r"(_arg2), "r"(_arg3),                                     \
84 		  "r"(_num)                                                   \
85 		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
86 	);                                                                    \
87 	_arg1;                                                                \
88 })
89 
90 #define my_syscall4(num, arg1, arg2, arg3, arg4)                              \
91 ({                                                                            \
92 	register long _num  __asm__ ("a7") = (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") = (long)(arg4);                    \
97 									      \
98 	__asm__ volatile (                                                    \
99 		"syscall 0\n"                                                 \
100 		: "+r"(_arg1)                                                 \
101 		: "r"(_arg2), "r"(_arg3), "r"(_arg4),                         \
102 		  "r"(_num)                                                   \
103 		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
104 	);                                                                    \
105 	_arg1;                                                                \
106 })
107 
108 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5)                        \
109 ({                                                                            \
110 	register long _num  __asm__ ("a7") = (num);                           \
111 	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
112 	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
113 	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
114 	register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
115 	register long _arg5 __asm__ ("a4") = (long)(arg5);                    \
116 									      \
117 	__asm__ volatile (                                                    \
118 		"syscall 0\n"                                                 \
119 		: "+r"(_arg1)                                                 \
120 		: "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5),             \
121 		  "r"(_num)                                                   \
122 		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
123 	);                                                                    \
124 	_arg1;                                                                \
125 })
126 
127 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)                  \
128 ({                                                                            \
129 	register long _num  __asm__ ("a7") = (num);                           \
130 	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
131 	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
132 	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
133 	register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
134 	register long _arg5 __asm__ ("a4") = (long)(arg5);                    \
135 	register long _arg6 __asm__ ("a5") = (long)(arg6);                    \
136 									      \
137 	__asm__ volatile (                                                    \
138 		"syscall 0\n"                                                 \
139 		: "+r"(_arg1)                                                 \
140 		: "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_arg6), \
141 		  "r"(_num)                                                   \
142 		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
143 	);                                                                    \
144 	_arg1;                                                                \
145 })
146 
147 #if __loongarch_grlen == 32
148 #define LONG_BSTRINS "bstrins.w"
149 #else /* __loongarch_grlen == 64 */
150 #define LONG_BSTRINS "bstrins.d"
151 #endif
152 
153 /* startup code */
154 void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector _start(void)
155 {
156 	__asm__ volatile (
157 		"move          $a0, $sp\n"         /* save stack pointer to $a0, as arg1 of _start_c */
158 		LONG_BSTRINS " $sp, $zero, 3, 0\n" /* $sp must be 16-byte aligned                    */
159 		"bl            _start_c\n"         /* transfer to c runtime                          */
160 	);
161 	__builtin_unreachable();
162 }
163 
164 #endif /* _NOLIBC_ARCH_LOONGARCH_H */
165