1 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */ 2 /* 3 * MIPS specific definitions for NOLIBC 4 * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu> 5 */ 6 7 #ifndef _NOLIBC_ARCH_MIPS_H 8 #define _NOLIBC_ARCH_MIPS_H 9 10 /* O_* macros for fcntl/open are architecture-specific */ 11 #define O_RDONLY 0 12 #define O_WRONLY 1 13 #define O_RDWR 2 14 #define O_APPEND 0x0008 15 #define O_NONBLOCK 0x0080 16 #define O_CREAT 0x0100 17 #define O_TRUNC 0x0200 18 #define O_EXCL 0x0400 19 #define O_NOCTTY 0x0800 20 #define O_DIRECTORY 0x10000 21 22 /* The struct returned by the stat() syscall. 88 bytes are returned by the 23 * syscall. 24 */ 25 struct sys_stat_struct { 26 unsigned int st_dev; 27 long st_pad1[3]; 28 unsigned long st_ino; 29 unsigned int st_mode; 30 unsigned int st_nlink; 31 unsigned int st_uid; 32 unsigned int st_gid; 33 unsigned int st_rdev; 34 long st_pad2[2]; 35 long st_size; 36 long st_pad3; 37 38 long st_atime; 39 long st_atime_nsec; 40 long st_mtime; 41 long st_mtime_nsec; 42 43 long st_ctime; 44 long st_ctime_nsec; 45 long st_blksize; 46 long st_blocks; 47 long st_pad4[14]; 48 }; 49 50 /* Syscalls for MIPS ABI O32 : 51 * - WARNING! there's always a delayed slot! 52 * - WARNING again, the syntax is different, registers take a '$' and numbers 53 * do not. 54 * - registers are 32-bit 55 * - stack is 8-byte aligned 56 * - syscall number is passed in v0 (starts at 0xfa0). 57 * - arguments are in a0, a1, a2, a3, then the stack. The caller needs to 58 * leave some room in the stack for the callee to save a0..a3 if needed. 59 * - Many registers are clobbered, in fact only a0..a2 and s0..s8 are 60 * preserved. See: https://www.linux-mips.org/wiki/Syscall as well as 61 * scall32-o32.S in the kernel sources. 62 * - the system call is performed by calling "syscall" 63 * - syscall return comes in v0, and register a3 needs to be checked to know 64 * if an error occurred, in which case errno is in v0. 65 * - the arguments are cast to long and assigned into the target registers 66 * which are then simply passed as registers to the asm code, so that we 67 * don't have to experience issues with register constraints. 68 */ 69 70 #define my_syscall0(num) \ 71 ({ \ 72 register long _num __asm__ ("v0") = (num); \ 73 register long _arg4 __asm__ ("a3"); \ 74 \ 75 __asm__ volatile ( \ 76 "addiu $sp, $sp, -32\n" \ 77 "syscall\n" \ 78 "addiu $sp, $sp, 32\n" \ 79 : "=r"(_num), "=r"(_arg4) \ 80 : "r"(_num) \ 81 : "memory", "cc", "at", "v1", "hi", "lo", \ 82 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ 83 ); \ 84 _arg4 ? -_num : _num; \ 85 }) 86 87 #define my_syscall1(num, arg1) \ 88 ({ \ 89 register long _num __asm__ ("v0") = (num); \ 90 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 91 register long _arg4 __asm__ ("a3"); \ 92 \ 93 __asm__ volatile ( \ 94 "addiu $sp, $sp, -32\n" \ 95 "syscall\n" \ 96 "addiu $sp, $sp, 32\n" \ 97 : "=r"(_num), "=r"(_arg4) \ 98 : "0"(_num), \ 99 "r"(_arg1) \ 100 : "memory", "cc", "at", "v1", "hi", "lo", \ 101 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ 102 ); \ 103 _arg4 ? -_num : _num; \ 104 }) 105 106 #define my_syscall2(num, arg1, arg2) \ 107 ({ \ 108 register long _num __asm__ ("v0") = (num); \ 109 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 110 register long _arg2 __asm__ ("a1") = (long)(arg2); \ 111 register long _arg4 __asm__ ("a3"); \ 112 \ 113 __asm__ volatile ( \ 114 "addiu $sp, $sp, -32\n" \ 115 "syscall\n" \ 116 "addiu $sp, $sp, 32\n" \ 117 : "=r"(_num), "=r"(_arg4) \ 118 : "0"(_num), \ 119 "r"(_arg1), "r"(_arg2) \ 120 : "memory", "cc", "at", "v1", "hi", "lo", \ 121 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ 122 ); \ 123 _arg4 ? -_num : _num; \ 124 }) 125 126 #define my_syscall3(num, arg1, arg2, arg3) \ 127 ({ \ 128 register long _num __asm__ ("v0") = (num); \ 129 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 130 register long _arg2 __asm__ ("a1") = (long)(arg2); \ 131 register long _arg3 __asm__ ("a2") = (long)(arg3); \ 132 register long _arg4 __asm__ ("a3"); \ 133 \ 134 __asm__ volatile ( \ 135 "addiu $sp, $sp, -32\n" \ 136 "syscall\n" \ 137 "addiu $sp, $sp, 32\n" \ 138 : "=r"(_num), "=r"(_arg4) \ 139 : "0"(_num), \ 140 "r"(_arg1), "r"(_arg2), "r"(_arg3) \ 141 : "memory", "cc", "at", "v1", "hi", "lo", \ 142 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ 143 ); \ 144 _arg4 ? -_num : _num; \ 145 }) 146 147 #define my_syscall4(num, arg1, arg2, arg3, arg4) \ 148 ({ \ 149 register long _num __asm__ ("v0") = (num); \ 150 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 151 register long _arg2 __asm__ ("a1") = (long)(arg2); \ 152 register long _arg3 __asm__ ("a2") = (long)(arg3); \ 153 register long _arg4 __asm__ ("a3") = (long)(arg4); \ 154 \ 155 __asm__ volatile ( \ 156 "addiu $sp, $sp, -32\n" \ 157 "syscall\n" \ 158 "addiu $sp, $sp, 32\n" \ 159 : "=r" (_num), "=r"(_arg4) \ 160 : "0"(_num), \ 161 "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \ 162 : "memory", "cc", "at", "v1", "hi", "lo", \ 163 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ 164 ); \ 165 _arg4 ? -_num : _num; \ 166 }) 167 168 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ 169 ({ \ 170 register long _num __asm__ ("v0") = (num); \ 171 register long _arg1 __asm__ ("a0") = (long)(arg1); \ 172 register long _arg2 __asm__ ("a1") = (long)(arg2); \ 173 register long _arg3 __asm__ ("a2") = (long)(arg3); \ 174 register long _arg4 __asm__ ("a3") = (long)(arg4); \ 175 register long _arg5 = (long)(arg5); \ 176 \ 177 __asm__ volatile ( \ 178 "addiu $sp, $sp, -32\n" \ 179 "sw %7, 16($sp)\n" \ 180 "syscall\n " \ 181 "addiu $sp, $sp, 32\n" \ 182 : "=r" (_num), "=r"(_arg4) \ 183 : "0"(_num), \ 184 "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5) \ 185 : "memory", "cc", "at", "v1", "hi", "lo", \ 186 "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9" \ 187 ); \ 188 _arg4 ? -_num : _num; \ 189 }) 190 191 /* startup code, note that it's called __start on MIPS */ 192 __asm__ (".section .text\n" 193 ".weak __start\n" 194 ".set nomips16\n" 195 ".set push\n" 196 ".set noreorder\n" 197 ".option pic0\n" 198 ".ent __start\n" 199 "__start:\n" 200 "lw $a0,($sp)\n" // argc was in the stack 201 "addiu $a1, $sp, 4\n" // argv = sp + 4 202 "sll $a2, $a0, 2\n" // a2 = argc * 4 203 "add $a2, $a2, $a1\n" // envp = argv + 4*argc ... 204 "addiu $a2, $a2, 4\n" // ... + 4 205 "li $t0, -8\n" 206 "and $sp, $sp, $t0\n" // sp must be 8-byte aligned 207 "addiu $sp,$sp,-16\n" // the callee expects to save a0..a3 there! 208 "jal main\n" // main() returns the status code, we'll exit with it. 209 "nop\n" // delayed slot 210 "move $a0, $v0\n" // retrieve 32-bit exit code from v0 211 "li $v0, 4001\n" // NR_exit == 4001 212 "syscall\n" 213 ".end __start\n" 214 ".set pop\n" 215 ""); 216 217 #endif // _NOLIBC_ARCH_MIPS_H 218