207 lines · c
1/* SPDX-License-Identifier: LGPL-2.1 OR MIT */2/*3 * MIPS specific definitions for NOLIBC4 * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>5 */6 7#ifndef _NOLIBC_ARCH_MIPS_H8#define _NOLIBC_ARCH_MIPS_H9 10#include "compiler.h"11#include "crt.h"12 13#if !defined(_ABIO32)14#error Unsupported MIPS ABI15#endif16 17/* Syscalls for MIPS ABI O32 :18 * - WARNING! there's always a delayed slot!19 * - WARNING again, the syntax is different, registers take a '$' and numbers20 * do not.21 * - registers are 32-bit22 * - stack is 8-byte aligned23 * - syscall number is passed in v0 (starts at 0xfa0).24 * - arguments are in a0, a1, a2, a3, then the stack. The caller needs to25 * leave some room in the stack for the callee to save a0..a3 if needed.26 * - Many registers are clobbered, in fact only a0..a2 and s0..s8 are27 * preserved. See: https://www.linux-mips.org/wiki/Syscall as well as28 * scall32-o32.S in the kernel sources.29 * - the system call is performed by calling "syscall"30 * - syscall return comes in v0, and register a3 needs to be checked to know31 * if an error occurred, in which case errno is in v0.32 * - the arguments are cast to long and assigned into the target registers33 * which are then simply passed as registers to the asm code, so that we34 * don't have to experience issues with register constraints.35 */36 37#define _NOLIBC_SYSCALL_CLOBBERLIST \38 "memory", "cc", "at", "v1", "hi", "lo", \39 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"40 41#define my_syscall0(num) \42({ \43 register long _num __asm__ ("v0") = (num); \44 register long _arg4 __asm__ ("a3"); \45 \46 __asm__ volatile ( \47 "addiu $sp, $sp, -32\n" \48 "syscall\n" \49 "addiu $sp, $sp, 32\n" \50 : "=r"(_num), "=r"(_arg4) \51 : "r"(_num) \52 : _NOLIBC_SYSCALL_CLOBBERLIST \53 ); \54 _arg4 ? -_num : _num; \55})56 57#define my_syscall1(num, arg1) \58({ \59 register long _num __asm__ ("v0") = (num); \60 register long _arg1 __asm__ ("a0") = (long)(arg1); \61 register long _arg4 __asm__ ("a3"); \62 \63 __asm__ volatile ( \64 "addiu $sp, $sp, -32\n" \65 "syscall\n" \66 "addiu $sp, $sp, 32\n" \67 : "=r"(_num), "=r"(_arg4) \68 : "0"(_num), \69 "r"(_arg1) \70 : _NOLIBC_SYSCALL_CLOBBERLIST \71 ); \72 _arg4 ? -_num : _num; \73})74 75#define my_syscall2(num, arg1, arg2) \76({ \77 register long _num __asm__ ("v0") = (num); \78 register long _arg1 __asm__ ("a0") = (long)(arg1); \79 register long _arg2 __asm__ ("a1") = (long)(arg2); \80 register long _arg4 __asm__ ("a3"); \81 \82 __asm__ volatile ( \83 "addiu $sp, $sp, -32\n" \84 "syscall\n" \85 "addiu $sp, $sp, 32\n" \86 : "=r"(_num), "=r"(_arg4) \87 : "0"(_num), \88 "r"(_arg1), "r"(_arg2) \89 : _NOLIBC_SYSCALL_CLOBBERLIST \90 ); \91 _arg4 ? -_num : _num; \92})93 94#define my_syscall3(num, arg1, arg2, arg3) \95({ \96 register long _num __asm__ ("v0") = (num); \97 register long _arg1 __asm__ ("a0") = (long)(arg1); \98 register long _arg2 __asm__ ("a1") = (long)(arg2); \99 register long _arg3 __asm__ ("a2") = (long)(arg3); \100 register long _arg4 __asm__ ("a3"); \101 \102 __asm__ volatile ( \103 "addiu $sp, $sp, -32\n" \104 "syscall\n" \105 "addiu $sp, $sp, 32\n" \106 : "=r"(_num), "=r"(_arg4) \107 : "0"(_num), \108 "r"(_arg1), "r"(_arg2), "r"(_arg3) \109 : _NOLIBC_SYSCALL_CLOBBERLIST \110 ); \111 _arg4 ? -_num : _num; \112})113 114#define my_syscall4(num, arg1, arg2, arg3, arg4) \115({ \116 register long _num __asm__ ("v0") = (num); \117 register long _arg1 __asm__ ("a0") = (long)(arg1); \118 register long _arg2 __asm__ ("a1") = (long)(arg2); \119 register long _arg3 __asm__ ("a2") = (long)(arg3); \120 register long _arg4 __asm__ ("a3") = (long)(arg4); \121 \122 __asm__ volatile ( \123 "addiu $sp, $sp, -32\n" \124 "syscall\n" \125 "addiu $sp, $sp, 32\n" \126 : "=r" (_num), "=r"(_arg4) \127 : "0"(_num), \128 "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \129 : _NOLIBC_SYSCALL_CLOBBERLIST \130 ); \131 _arg4 ? -_num : _num; \132})133 134#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \135({ \136 register long _num __asm__ ("v0") = (num); \137 register long _arg1 __asm__ ("a0") = (long)(arg1); \138 register long _arg2 __asm__ ("a1") = (long)(arg2); \139 register long _arg3 __asm__ ("a2") = (long)(arg3); \140 register long _arg4 __asm__ ("a3") = (long)(arg4); \141 register long _arg5 = (long)(arg5); \142 \143 __asm__ volatile ( \144 "addiu $sp, $sp, -32\n" \145 "sw %7, 16($sp)\n" \146 "syscall\n" \147 "addiu $sp, $sp, 32\n" \148 : "=r" (_num), "=r"(_arg4) \149 : "0"(_num), \150 "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \151 : _NOLIBC_SYSCALL_CLOBBERLIST \152 ); \153 _arg4 ? -_num : _num; \154})155 156#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \157({ \158 register long _num __asm__ ("v0") = (num); \159 register long _arg1 __asm__ ("a0") = (long)(arg1); \160 register long _arg2 __asm__ ("a1") = (long)(arg2); \161 register long _arg3 __asm__ ("a2") = (long)(arg3); \162 register long _arg4 __asm__ ("a3") = (long)(arg4); \163 register long _arg5 = (long)(arg5); \164 register long _arg6 = (long)(arg6); \165 \166 __asm__ volatile ( \167 "addiu $sp, $sp, -32\n" \168 "sw %7, 16($sp)\n" \169 "sw %8, 20($sp)\n" \170 "syscall\n" \171 "addiu $sp, $sp, 32\n" \172 : "=r" (_num), "=r"(_arg4) \173 : "0"(_num), \174 "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \175 "r"(_arg6) \176 : _NOLIBC_SYSCALL_CLOBBERLIST \177 ); \178 _arg4 ? -_num : _num; \179})180 181/* startup code, note that it's called __start on MIPS */182void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector __start(void)183{184 __asm__ volatile (185 ".set push\n"186 ".set noreorder\n"187 "bal 1f\n" /* prime $ra for .cpload */188 "nop\n"189 "1:\n"190 ".cpload $ra\n"191 "move $a0, $sp\n" /* save stack pointer to $a0, as arg1 of _start_c */192 "addiu $sp, $sp, -4\n" /* space for .cprestore to store $gp */193 ".cprestore 0\n"194 "li $t0, -8\n"195 "and $sp, $sp, $t0\n" /* $sp must be 8-byte aligned */196 "addiu $sp, $sp, -16\n" /* the callee expects to save a0..a3 there */197 "lui $t9, %hi(_start_c)\n" /* ABI requires current function address in $t9 */198 "ori $t9, %lo(_start_c)\n"199 "jalr $t9\n" /* transfer to c runtime */200 " nop\n" /* delayed slot */201 ".set pop\n"202 );203 __nolibc_entrypoint_epilogue();204}205 206#endif /* _NOLIBC_ARCH_MIPS_H */207