162306a36Sopenharmony_ci/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * MIPS specific definitions for NOLIBC
462306a36Sopenharmony_ci * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#ifndef _NOLIBC_ARCH_MIPS_H
862306a36Sopenharmony_ci#define _NOLIBC_ARCH_MIPS_H
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "compiler.h"
1162306a36Sopenharmony_ci#include "crt.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/* Syscalls for MIPS ABI O32 :
1462306a36Sopenharmony_ci *   - WARNING! there's always a delayed slot!
1562306a36Sopenharmony_ci *   - WARNING again, the syntax is different, registers take a '$' and numbers
1662306a36Sopenharmony_ci *     do not.
1762306a36Sopenharmony_ci *   - registers are 32-bit
1862306a36Sopenharmony_ci *   - stack is 8-byte aligned
1962306a36Sopenharmony_ci *   - syscall number is passed in v0 (starts at 0xfa0).
2062306a36Sopenharmony_ci *   - arguments are in a0, a1, a2, a3, then the stack. The caller needs to
2162306a36Sopenharmony_ci *     leave some room in the stack for the callee to save a0..a3 if needed.
2262306a36Sopenharmony_ci *   - Many registers are clobbered, in fact only a0..a2 and s0..s8 are
2362306a36Sopenharmony_ci *     preserved. See: https://www.linux-mips.org/wiki/Syscall as well as
2462306a36Sopenharmony_ci *     scall32-o32.S in the kernel sources.
2562306a36Sopenharmony_ci *   - the system call is performed by calling "syscall"
2662306a36Sopenharmony_ci *   - syscall return comes in v0, and register a3 needs to be checked to know
2762306a36Sopenharmony_ci *     if an error occurred, in which case errno is in v0.
2862306a36Sopenharmony_ci *   - the arguments are cast to long and assigned into the target registers
2962306a36Sopenharmony_ci *     which are then simply passed as registers to the asm code, so that we
3062306a36Sopenharmony_ci *     don't have to experience issues with register constraints.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define _NOLIBC_SYSCALL_CLOBBERLIST \
3462306a36Sopenharmony_ci	"memory", "cc", "at", "v1", "hi", "lo", \
3562306a36Sopenharmony_ci	"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define my_syscall0(num)                                                      \
3862306a36Sopenharmony_ci({                                                                            \
3962306a36Sopenharmony_ci	register long _num __asm__ ("v0") = (num);                            \
4062306a36Sopenharmony_ci	register long _arg4 __asm__ ("a3");                                   \
4162306a36Sopenharmony_ci									      \
4262306a36Sopenharmony_ci	__asm__ volatile (                                                    \
4362306a36Sopenharmony_ci		"addiu $sp, $sp, -32\n"                                       \
4462306a36Sopenharmony_ci		"syscall\n"                                                   \
4562306a36Sopenharmony_ci		"addiu $sp, $sp, 32\n"                                        \
4662306a36Sopenharmony_ci		: "=r"(_num), "=r"(_arg4)                                     \
4762306a36Sopenharmony_ci		: "r"(_num)                                                   \
4862306a36Sopenharmony_ci		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
4962306a36Sopenharmony_ci	);                                                                    \
5062306a36Sopenharmony_ci	_arg4 ? -_num : _num;                                                 \
5162306a36Sopenharmony_ci})
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#define my_syscall1(num, arg1)                                                \
5462306a36Sopenharmony_ci({                                                                            \
5562306a36Sopenharmony_ci	register long _num __asm__ ("v0") = (num);                            \
5662306a36Sopenharmony_ci	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
5762306a36Sopenharmony_ci	register long _arg4 __asm__ ("a3");                                   \
5862306a36Sopenharmony_ci									      \
5962306a36Sopenharmony_ci	__asm__ volatile (                                                    \
6062306a36Sopenharmony_ci		"addiu $sp, $sp, -32\n"                                       \
6162306a36Sopenharmony_ci		"syscall\n"                                                   \
6262306a36Sopenharmony_ci		"addiu $sp, $sp, 32\n"                                        \
6362306a36Sopenharmony_ci		: "=r"(_num), "=r"(_arg4)                                     \
6462306a36Sopenharmony_ci		: "0"(_num),                                                  \
6562306a36Sopenharmony_ci		  "r"(_arg1)                                                  \
6662306a36Sopenharmony_ci		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
6762306a36Sopenharmony_ci	);                                                                    \
6862306a36Sopenharmony_ci	_arg4 ? -_num : _num;                                                 \
6962306a36Sopenharmony_ci})
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define my_syscall2(num, arg1, arg2)                                          \
7262306a36Sopenharmony_ci({                                                                            \
7362306a36Sopenharmony_ci	register long _num __asm__ ("v0") = (num);                            \
7462306a36Sopenharmony_ci	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
7562306a36Sopenharmony_ci	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
7662306a36Sopenharmony_ci	register long _arg4 __asm__ ("a3");                                   \
7762306a36Sopenharmony_ci									      \
7862306a36Sopenharmony_ci	__asm__ volatile (                                                    \
7962306a36Sopenharmony_ci		"addiu $sp, $sp, -32\n"                                       \
8062306a36Sopenharmony_ci		"syscall\n"                                                   \
8162306a36Sopenharmony_ci		"addiu $sp, $sp, 32\n"                                        \
8262306a36Sopenharmony_ci		: "=r"(_num), "=r"(_arg4)                                     \
8362306a36Sopenharmony_ci		: "0"(_num),                                                  \
8462306a36Sopenharmony_ci		  "r"(_arg1), "r"(_arg2)                                      \
8562306a36Sopenharmony_ci		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
8662306a36Sopenharmony_ci	);                                                                    \
8762306a36Sopenharmony_ci	_arg4 ? -_num : _num;                                                 \
8862306a36Sopenharmony_ci})
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define my_syscall3(num, arg1, arg2, arg3)                                    \
9162306a36Sopenharmony_ci({                                                                            \
9262306a36Sopenharmony_ci	register long _num __asm__ ("v0")  = (num);                           \
9362306a36Sopenharmony_ci	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
9462306a36Sopenharmony_ci	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
9562306a36Sopenharmony_ci	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
9662306a36Sopenharmony_ci	register long _arg4 __asm__ ("a3");                                   \
9762306a36Sopenharmony_ci									      \
9862306a36Sopenharmony_ci	__asm__ volatile (                                                    \
9962306a36Sopenharmony_ci		"addiu $sp, $sp, -32\n"                                       \
10062306a36Sopenharmony_ci		"syscall\n"                                                   \
10162306a36Sopenharmony_ci		"addiu $sp, $sp, 32\n"                                        \
10262306a36Sopenharmony_ci		: "=r"(_num), "=r"(_arg4)                                     \
10362306a36Sopenharmony_ci		: "0"(_num),                                                  \
10462306a36Sopenharmony_ci		  "r"(_arg1), "r"(_arg2), "r"(_arg3)                          \
10562306a36Sopenharmony_ci		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
10662306a36Sopenharmony_ci	);                                                                    \
10762306a36Sopenharmony_ci	_arg4 ? -_num : _num;                                                 \
10862306a36Sopenharmony_ci})
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci#define my_syscall4(num, arg1, arg2, arg3, arg4)                              \
11162306a36Sopenharmony_ci({                                                                            \
11262306a36Sopenharmony_ci	register long _num __asm__ ("v0") = (num);                            \
11362306a36Sopenharmony_ci	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
11462306a36Sopenharmony_ci	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
11562306a36Sopenharmony_ci	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
11662306a36Sopenharmony_ci	register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
11762306a36Sopenharmony_ci									      \
11862306a36Sopenharmony_ci	__asm__ volatile (                                                    \
11962306a36Sopenharmony_ci		"addiu $sp, $sp, -32\n"                                       \
12062306a36Sopenharmony_ci		"syscall\n"                                                   \
12162306a36Sopenharmony_ci		"addiu $sp, $sp, 32\n"                                        \
12262306a36Sopenharmony_ci		: "=r" (_num), "=r"(_arg4)                                    \
12362306a36Sopenharmony_ci		: "0"(_num),                                                  \
12462306a36Sopenharmony_ci		  "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4)              \
12562306a36Sopenharmony_ci		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
12662306a36Sopenharmony_ci	);                                                                    \
12762306a36Sopenharmony_ci	_arg4 ? -_num : _num;                                                 \
12862306a36Sopenharmony_ci})
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5)                        \
13162306a36Sopenharmony_ci({                                                                            \
13262306a36Sopenharmony_ci	register long _num __asm__ ("v0") = (num);                            \
13362306a36Sopenharmony_ci	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
13462306a36Sopenharmony_ci	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
13562306a36Sopenharmony_ci	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
13662306a36Sopenharmony_ci	register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
13762306a36Sopenharmony_ci	register long _arg5 = (long)(arg5);                                   \
13862306a36Sopenharmony_ci									      \
13962306a36Sopenharmony_ci	__asm__ volatile (                                                    \
14062306a36Sopenharmony_ci		"addiu $sp, $sp, -32\n"                                       \
14162306a36Sopenharmony_ci		"sw %7, 16($sp)\n"                                            \
14262306a36Sopenharmony_ci		"syscall\n"                                                   \
14362306a36Sopenharmony_ci		"addiu $sp, $sp, 32\n"                                        \
14462306a36Sopenharmony_ci		: "=r" (_num), "=r"(_arg4)                                    \
14562306a36Sopenharmony_ci		: "0"(_num),                                                  \
14662306a36Sopenharmony_ci		  "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5)  \
14762306a36Sopenharmony_ci		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
14862306a36Sopenharmony_ci	);                                                                    \
14962306a36Sopenharmony_ci	_arg4 ? -_num : _num;                                                 \
15062306a36Sopenharmony_ci})
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)                  \
15362306a36Sopenharmony_ci({                                                                            \
15462306a36Sopenharmony_ci	register long _num __asm__ ("v0")  = (num);                           \
15562306a36Sopenharmony_ci	register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
15662306a36Sopenharmony_ci	register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
15762306a36Sopenharmony_ci	register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
15862306a36Sopenharmony_ci	register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
15962306a36Sopenharmony_ci	register long _arg5 = (long)(arg5);                                   \
16062306a36Sopenharmony_ci	register long _arg6 = (long)(arg6);                                   \
16162306a36Sopenharmony_ci									      \
16262306a36Sopenharmony_ci	__asm__ volatile (                                                    \
16362306a36Sopenharmony_ci		"addiu $sp, $sp, -32\n"                                       \
16462306a36Sopenharmony_ci		"sw %7, 16($sp)\n"                                            \
16562306a36Sopenharmony_ci		"sw %8, 20($sp)\n"                                            \
16662306a36Sopenharmony_ci		"syscall\n"                                                   \
16762306a36Sopenharmony_ci		"addiu $sp, $sp, 32\n"                                        \
16862306a36Sopenharmony_ci		: "=r" (_num), "=r"(_arg4)                                    \
16962306a36Sopenharmony_ci		: "0"(_num),                                                  \
17062306a36Sopenharmony_ci		  "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \
17162306a36Sopenharmony_ci		  "r"(_arg6)                                                  \
17262306a36Sopenharmony_ci		: _NOLIBC_SYSCALL_CLOBBERLIST                                 \
17362306a36Sopenharmony_ci	);                                                                    \
17462306a36Sopenharmony_ci	_arg4 ? -_num : _num;                                                 \
17562306a36Sopenharmony_ci})
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/* startup code, note that it's called __start on MIPS */
17862306a36Sopenharmony_civoid __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_stack_protector __start(void)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	__asm__ volatile (
18162306a36Sopenharmony_ci		".set push\n"
18262306a36Sopenharmony_ci		".set noreorder\n"
18362306a36Sopenharmony_ci		".option pic0\n"
18462306a36Sopenharmony_ci		"move  $a0, $sp\n"       /* save stack pointer to $a0, as arg1 of _start_c */
18562306a36Sopenharmony_ci		"li    $t0, -8\n"
18662306a36Sopenharmony_ci		"and   $sp, $sp, $t0\n"  /* $sp must be 8-byte aligned                     */
18762306a36Sopenharmony_ci		"addiu $sp, $sp, -16\n"  /* the callee expects to save a0..a3 there        */
18862306a36Sopenharmony_ci		"jal   _start_c\n"       /* transfer to c runtime                          */
18962306a36Sopenharmony_ci		" nop\n"                 /* delayed slot                                   */
19062306a36Sopenharmony_ci		".set pop\n"
19162306a36Sopenharmony_ci	);
19262306a36Sopenharmony_ci	__builtin_unreachable();
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci#endif /* _NOLIBC_ARCH_MIPS_H */
196