Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: LGPL-2.1 OR MIT */
0002 /*
0003  * MIPS specific definitions for NOLIBC
0004  * Copyright (C) 2017-2022 Willy Tarreau <w@1wt.eu>
0005  */
0006 
0007 #ifndef _NOLIBC_ARCH_MIPS_H
0008 #define _NOLIBC_ARCH_MIPS_H
0009 
0010 /* O_* macros for fcntl/open are architecture-specific */
0011 #define O_RDONLY            0
0012 #define O_WRONLY            1
0013 #define O_RDWR              2
0014 #define O_APPEND       0x0008
0015 #define O_NONBLOCK     0x0080
0016 #define O_CREAT        0x0100
0017 #define O_TRUNC        0x0200
0018 #define O_EXCL         0x0400
0019 #define O_NOCTTY       0x0800
0020 #define O_DIRECTORY   0x10000
0021 
0022 /* The struct returned by the stat() syscall. 88 bytes are returned by the
0023  * syscall.
0024  */
0025 struct sys_stat_struct {
0026     unsigned int  st_dev;
0027     long          st_pad1[3];
0028     unsigned long st_ino;
0029     unsigned int  st_mode;
0030     unsigned int  st_nlink;
0031     unsigned int  st_uid;
0032     unsigned int  st_gid;
0033     unsigned int  st_rdev;
0034     long          st_pad2[2];
0035     long          st_size;
0036     long          st_pad3;
0037 
0038     long          st_atime;
0039     long          st_atime_nsec;
0040     long          st_mtime;
0041     long          st_mtime_nsec;
0042 
0043     long          st_ctime;
0044     long          st_ctime_nsec;
0045     long          st_blksize;
0046     long          st_blocks;
0047     long          st_pad4[14];
0048 };
0049 
0050 /* Syscalls for MIPS ABI O32 :
0051  *   - WARNING! there's always a delayed slot!
0052  *   - WARNING again, the syntax is different, registers take a '$' and numbers
0053  *     do not.
0054  *   - registers are 32-bit
0055  *   - stack is 8-byte aligned
0056  *   - syscall number is passed in v0 (starts at 0xfa0).
0057  *   - arguments are in a0, a1, a2, a3, then the stack. The caller needs to
0058  *     leave some room in the stack for the callee to save a0..a3 if needed.
0059  *   - Many registers are clobbered, in fact only a0..a2 and s0..s8 are
0060  *     preserved. See: https://www.linux-mips.org/wiki/Syscall as well as
0061  *     scall32-o32.S in the kernel sources.
0062  *   - the system call is performed by calling "syscall"
0063  *   - syscall return comes in v0, and register a3 needs to be checked to know
0064  *     if an error occurred, in which case errno is in v0.
0065  *   - the arguments are cast to long and assigned into the target registers
0066  *     which are then simply passed as registers to the asm code, so that we
0067  *     don't have to experience issues with register constraints.
0068  */
0069 
0070 #define my_syscall0(num)                                                      \
0071 ({                                                                            \
0072     register long _num __asm__ ("v0") = (num);                            \
0073     register long _arg4 __asm__ ("a3");                                   \
0074                                                                           \
0075     __asm__  volatile (                                                   \
0076         "addiu $sp, $sp, -32\n"                                       \
0077         "syscall\n"                                                   \
0078         "addiu $sp, $sp, 32\n"                                        \
0079         : "=r"(_num), "=r"(_arg4)                                     \
0080         : "r"(_num)                                                   \
0081         : "memory", "cc", "at", "v1", "hi", "lo",                     \
0082               "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
0083     );                                                                    \
0084     _arg4 ? -_num : _num;                                                 \
0085 })
0086 
0087 #define my_syscall1(num, arg1)                                                \
0088 ({                                                                            \
0089     register long _num __asm__ ("v0") = (num);                            \
0090     register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
0091     register long _arg4 __asm__ ("a3");                                   \
0092                                                                           \
0093     __asm__  volatile (                                                   \
0094         "addiu $sp, $sp, -32\n"                                       \
0095         "syscall\n"                                                   \
0096         "addiu $sp, $sp, 32\n"                                        \
0097         : "=r"(_num), "=r"(_arg4)                                     \
0098         : "0"(_num),                                                  \
0099           "r"(_arg1)                                                  \
0100         : "memory", "cc", "at", "v1", "hi", "lo",                     \
0101               "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
0102     );                                                                    \
0103     _arg4 ? -_num : _num;                                                 \
0104 })
0105 
0106 #define my_syscall2(num, arg1, arg2)                                          \
0107 ({                                                                            \
0108     register long _num __asm__ ("v0") = (num);                            \
0109     register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
0110     register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
0111     register long _arg4 __asm__ ("a3");                                   \
0112                                                                           \
0113     __asm__  volatile (                                                   \
0114         "addiu $sp, $sp, -32\n"                                       \
0115         "syscall\n"                                                   \
0116         "addiu $sp, $sp, 32\n"                                        \
0117         : "=r"(_num), "=r"(_arg4)                                     \
0118         : "0"(_num),                                                  \
0119           "r"(_arg1), "r"(_arg2)                                      \
0120         : "memory", "cc", "at", "v1", "hi", "lo",                     \
0121               "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
0122     );                                                                    \
0123     _arg4 ? -_num : _num;                                                 \
0124 })
0125 
0126 #define my_syscall3(num, arg1, arg2, arg3)                                    \
0127 ({                                                                            \
0128     register long _num __asm__ ("v0")  = (num);                           \
0129     register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
0130     register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
0131     register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
0132     register long _arg4 __asm__ ("a3");                                   \
0133                                                                           \
0134     __asm__  volatile (                                                   \
0135         "addiu $sp, $sp, -32\n"                                       \
0136         "syscall\n"                                                   \
0137         "addiu $sp, $sp, 32\n"                                        \
0138         : "=r"(_num), "=r"(_arg4)                                     \
0139         : "0"(_num),                                                  \
0140           "r"(_arg1), "r"(_arg2), "r"(_arg3)                          \
0141         : "memory", "cc", "at", "v1", "hi", "lo",                     \
0142               "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
0143     );                                                                    \
0144     _arg4 ? -_num : _num;                                                 \
0145 })
0146 
0147 #define my_syscall4(num, arg1, arg2, arg3, arg4)                              \
0148 ({                                                                            \
0149     register long _num __asm__ ("v0") = (num);                            \
0150     register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
0151     register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
0152     register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
0153     register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
0154                                                                           \
0155     __asm__  volatile (                                                   \
0156         "addiu $sp, $sp, -32\n"                                       \
0157         "syscall\n"                                                   \
0158         "addiu $sp, $sp, 32\n"                                        \
0159         : "=r" (_num), "=r"(_arg4)                                    \
0160         : "0"(_num),                                                  \
0161           "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4)              \
0162         : "memory", "cc", "at", "v1", "hi", "lo",                     \
0163               "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
0164     );                                                                    \
0165     _arg4 ? -_num : _num;                                                 \
0166 })
0167 
0168 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5)                        \
0169 ({                                                                            \
0170     register long _num __asm__ ("v0") = (num);                            \
0171     register long _arg1 __asm__ ("a0") = (long)(arg1);                    \
0172     register long _arg2 __asm__ ("a1") = (long)(arg2);                    \
0173     register long _arg3 __asm__ ("a2") = (long)(arg3);                    \
0174     register long _arg4 __asm__ ("a3") = (long)(arg4);                    \
0175     register long _arg5 = (long)(arg5);                                   \
0176                                                                           \
0177     __asm__  volatile (                                                   \
0178         "addiu $sp, $sp, -32\n"                                       \
0179         "sw %7, 16($sp)\n"                                            \
0180         "syscall\n  "                                                 \
0181         "addiu $sp, $sp, 32\n"                                        \
0182         : "=r" (_num), "=r"(_arg4)                                    \
0183         : "0"(_num),                                                  \
0184           "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5)  \
0185         : "memory", "cc", "at", "v1", "hi", "lo",                     \
0186               "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9"  \
0187     );                                                                    \
0188     _arg4 ? -_num : _num;                                                 \
0189 })
0190 
0191 /* startup code, note that it's called __start on MIPS */
0192 __asm__ (".section .text\n"
0193     ".weak __start\n"
0194     ".set nomips16\n"
0195     ".set    noreorder\n"
0196     ".option pic0\n"
0197     ".ent __start\n"
0198     "__start:\n"
0199     "lw $a0,($sp)\n"              // argc was in the stack
0200     "addiu  $a1, $sp, 4\n"        // argv = sp + 4
0201     "sll $a2, $a0, 2\n"           // a2 = argc * 4
0202     "add   $a2, $a2, $a1\n"       // envp = argv + 4*argc ...
0203     "addiu $a2, $a2, 4\n"         //        ... + 4
0204     "li $t0, -8\n"
0205     "and $sp, $sp, $t0\n"         // sp must be 8-byte aligned
0206     "addiu $sp,$sp,-16\n"         // the callee expects to save a0..a3 there!
0207     "jal main\n"                  // main() returns the status code, we'll exit with it.
0208     "nop\n"                       // delayed slot
0209     "move $a0, $v0\n"             // retrieve 32-bit exit code from v0
0210     "li $v0, 4001\n"              // NR_exit == 4001
0211     "syscall\n"
0212     ".end __start\n"
0213     "");
0214 
0215 #endif // _NOLIBC_ARCH_MIPS_H