1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
2 /*
3 * SPARC (32bit and 64bit) specific definitions for NOLIBC
4 * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
5 */
6
7 #ifndef _NOLIBC_ARCH_SPARC_H
8 #define _NOLIBC_ARCH_SPARC_H
9
10 #include <linux/unistd.h>
11
12 #include "compiler.h"
13 #include "crt.h"
14
15 /*
16 * Syscalls for SPARC:
17 * - registers are native word size
18 * - syscall number is passed in g1
19 * - arguments are in o0-o5
20 * - the system call is performed by calling a trap instruction
21 * - syscall return value is in o0
22 * - syscall error flag is in the carry bit of the processor status register
23 */
24
25 #ifdef __arch64__
26
27 #define _NOLIBC_SYSCALL "t 0x6d\n" \
28 "bcs,a %%xcc, 1f\n" \
29 "sub %%g0, %%o0, %%o0\n" \
30 "1:\n"
31
32 #else
33
34 #define _NOLIBC_SYSCALL "t 0x10\n" \
35 "bcs,a 1f\n" \
36 "sub %%g0, %%o0, %%o0\n" \
37 "1:\n"
38
39 #endif /* __arch64__ */
40
41 #define my_syscall0(num) \
42 ({ \
43 register long _num __asm__ ("g1") = (num); \
44 register long _arg1 __asm__ ("o0"); \
45 \
46 __asm__ volatile ( \
47 _NOLIBC_SYSCALL \
48 : "+r"(_arg1) \
49 : "r"(_num) \
50 : "memory", "cc" \
51 ); \
52 _arg1; \
53 })
54
55 #define my_syscall1(num, arg1) \
56 ({ \
57 register long _num __asm__ ("g1") = (num); \
58 register long _arg1 __asm__ ("o0") = (long)(arg1); \
59 \
60 __asm__ volatile ( \
61 _NOLIBC_SYSCALL \
62 : "+r"(_arg1) \
63 : "r"(_num) \
64 : "memory", "cc" \
65 ); \
66 _arg1; \
67 })
68
69 #define my_syscall2(num, arg1, arg2) \
70 ({ \
71 register long _num __asm__ ("g1") = (num); \
72 register long _arg1 __asm__ ("o0") = (long)(arg1); \
73 register long _arg2 __asm__ ("o1") = (long)(arg2); \
74 \
75 __asm__ volatile ( \
76 _NOLIBC_SYSCALL \
77 : "+r"(_arg1) \
78 : "r"(_arg2), "r"(_num) \
79 : "memory", "cc" \
80 ); \
81 _arg1; \
82 })
83
84 #define my_syscall3(num, arg1, arg2, arg3) \
85 ({ \
86 register long _num __asm__ ("g1") = (num); \
87 register long _arg1 __asm__ ("o0") = (long)(arg1); \
88 register long _arg2 __asm__ ("o1") = (long)(arg2); \
89 register long _arg3 __asm__ ("o2") = (long)(arg3); \
90 \
91 __asm__ volatile ( \
92 _NOLIBC_SYSCALL \
93 : "+r"(_arg1) \
94 : "r"(_arg2), "r"(_arg3), "r"(_num) \
95 : "memory", "cc" \
96 ); \
97 _arg1; \
98 })
99
100 #define my_syscall4(num, arg1, arg2, arg3, arg4) \
101 ({ \
102 register long _num __asm__ ("g1") = (num); \
103 register long _arg1 __asm__ ("o0") = (long)(arg1); \
104 register long _arg2 __asm__ ("o1") = (long)(arg2); \
105 register long _arg3 __asm__ ("o2") = (long)(arg3); \
106 register long _arg4 __asm__ ("o3") = (long)(arg4); \
107 \
108 __asm__ volatile ( \
109 _NOLIBC_SYSCALL \
110 : "+r"(_arg1) \
111 : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_num) \
112 : "memory", "cc" \
113 ); \
114 _arg1; \
115 })
116
117 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \
118 ({ \
119 register long _num __asm__ ("g1") = (num); \
120 register long _arg1 __asm__ ("o0") = (long)(arg1); \
121 register long _arg2 __asm__ ("o1") = (long)(arg2); \
122 register long _arg3 __asm__ ("o2") = (long)(arg3); \
123 register long _arg4 __asm__ ("o3") = (long)(arg4); \
124 register long _arg5 __asm__ ("o4") = (long)(arg5); \
125 \
126 __asm__ volatile ( \
127 _NOLIBC_SYSCALL \
128 : "+r"(_arg1) \
129 : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_num) \
130 : "memory", "cc" \
131 ); \
132 _arg1; \
133 })
134
135 #define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \
136 ({ \
137 register long _num __asm__ ("g1") = (num); \
138 register long _arg1 __asm__ ("o0") = (long)(arg1); \
139 register long _arg2 __asm__ ("o1") = (long)(arg2); \
140 register long _arg3 __asm__ ("o2") = (long)(arg3); \
141 register long _arg4 __asm__ ("o3") = (long)(arg4); \
142 register long _arg5 __asm__ ("o4") = (long)(arg5); \
143 register long _arg6 __asm__ ("o5") = (long)(arg6); \
144 \
145 __asm__ volatile ( \
146 _NOLIBC_SYSCALL \
147 : "+r"(_arg1) \
148 : "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), "r"(_arg6), \
149 "r"(_num) \
150 : "memory", "cc" \
151 ); \
152 _arg1; \
153 })
154
155 /* startup code */
_start(void)156 void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector _start(void)
157 {
158 __asm__ volatile (
159 /*
160 * Save argc pointer to o0, as arg1 of _start_c.
161 * Account for the window save area, which is 16 registers wide.
162 */
163 #ifdef __arch64__
164 "add %sp, 128 + 2047, %o0\n" /* on sparc64 / v9 the stack is offset by 2047 */
165 #else
166 "add %sp, 64, %o0\n"
167 #endif
168 "b,a _start_c\n" /* transfer to c runtime */
169 );
170 __nolibc_entrypoint_epilogue();
171 }
172
173 static pid_t getpid(void);
174
175 static __attribute__((unused))
sys_fork(void)176 pid_t sys_fork(void)
177 {
178 pid_t parent, ret;
179
180 parent = getpid();
181 ret = my_syscall0(__NR_fork);
182
183 /* The syscall returns the parent pid in the child instead of 0 */
184 if (ret == parent)
185 return 0;
186 else
187 return ret;
188 }
189 #define sys_fork sys_fork
190
191 #endif /* _NOLIBC_ARCH_SPARC_H */
192