aboutsummaryrefslogtreecommitdiff
path: root/common-user/host/mips/safe-syscall.inc.S
blob: fc75a337d1620431ca385f9da1f43feacfa27d6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * safe-syscall.inc.S : host-specific assembly fragment
 * to handle signals occurring at the same time as system calls.
 * This is intended to be included by common-user/safe-syscall.S
 *
 * Written by Richard Henderson <richard.henderson@linaro.org>
 * Copyright (C) 2021 Linaro, Inc.
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

#include "sys/regdef.h"
#include "sys/asm.h"

        .text
        .set    nomips16
        .set    reorder

        .global safe_syscall_start
        .global safe_syscall_end
        .type   safe_syscall_start, @function
        .type   safe_syscall_end, @function

        /*
         * This is the entry point for making a system call. The calling
         * convention here is that of a C varargs function with the
         * first argument an 'int *' to the signal_pending flag, the
         * second one the system call number (as a 'long'), and all further
         * arguments being syscall arguments (also 'long').
         */

#if _MIPS_SIM == _ABIO32
/* 8 * 4 = 32 for outgoing parameters; 1 * 4 for s0 save; 1 * 4 for align. */
#define FRAME    40
#define OFS_S0   32
#else
/* 1 * 8 for s0 save; 1 * 8 for align. */
#define FRAME    16
#define OFS_S0   0
#endif


NESTED(safe_syscall_base, FRAME, ra)
        .cfi_startproc
        PTR_ADDIU sp, sp, -FRAME
        .cfi_adjust_cfa_offset FRAME
        REG_S   s0, OFS_S0(sp)
        .cfi_rel_offset s0, OFS_S0
#if _MIPS_SIM == _ABIO32
        /*
         * The syscall calling convention is nearly the same as C:
         * we enter with a0 == &signal_pending
         *               a1 == syscall number
         *               a2, a3, stack == syscall arguments
         *               and return the result in a0
         * and the syscall instruction needs
         *               v0 == syscall number
         *               a0 ... a3, stack == syscall arguments
         *               and returns the result in v0
         * Shuffle everything around appropriately.
         */
        move    s0, a0          /* signal_pending pointer */
        move    v0, a1          /* syscall number */
        move    a0, a2          /* syscall arguments */
        move    a1, a3
        lw      a2, FRAME+16(sp)
        lw      a3, FRAME+20(sp)
        lw      t4, FRAME+24(sp)
        lw      t5, FRAME+28(sp)
        lw      t6, FRAME+32(sp)
        lw      t7, FRAME+40(sp)
        sw      t4, 16(sp)
        sw      t5, 20(sp)
        sw      t6, 24(sp)
        sw      t7, 28(sp)
#else
        /*
         * The syscall calling convention is nearly the same as C:
         * we enter with a0 == &signal_pending
         *               a1 == syscall number
         *               a2 ... a7 == syscall arguments
         *               and return the result in a0
         * and the syscall instruction needs
         *               v0 == syscall number
         *               a0 ... a5 == syscall arguments
         *               and returns the result in v0
         * Shuffle everything around appropriately.
         */
        move    s0, a0          /* signal_pending pointer */
        move    v0, a1          /* syscall number */
        move    a0, a2          /* syscall arguments */
        move    a1, a3
        move    a2, a4
        move    a3, a5
        move    a4, a6
        move    a5, a7
#endif

        /*
         * This next sequence of code works in conjunction with the
         * rewind_if_safe_syscall_function(). If a signal is taken
         * and the interrupted PC is anywhere between 'safe_syscall_start'
         * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'.
         * The code sequence must therefore be able to cope with this, and
         * the syscall instruction must be the final one in the sequence.
         */
safe_syscall_start:
        /* If signal_pending is non-zero, don't do the call */
        lw      t1, 0(s0)
        bnez    t1, 2f
        syscall
safe_syscall_end:

        /* code path for having successfully executed the syscall */
        REG_L   s0, OFS_S0(sp)
        PTR_ADDIU sp, sp, FRAME
        .cfi_remember_state
        .cfi_adjust_cfa_offset -FRAME
        .cfi_restore s0
        bnez    a3, 1f
        jr      ra
        .cfi_restore_state

        /* code path when we didn't execute the syscall */
2:      REG_L   s0, OFS_S0(sp)
        PTR_ADDIU sp, sp, FRAME
        .cfi_adjust_cfa_offset -FRAME
        .cfi_restore s0
        li      v0, QEMU_ERESTARTSYS

        /* code path setting errno */
        /*
         * We didn't setup GP on entry, optimistic of the syscall success.
         * We must do so now to load the address of the helper, as required
         * by the ABI, into t9.
         *
         * Note that SETUP_GPX and SETUP_GPX64 are themselves conditional,
         * so we can simply let the one that's not empty succeed.
         */
1:      USE_ALT_CP(t0)
        SETUP_GPX(t1)
        SETUP_GPX64(t0, t1)
        PTR_LA  t9, safe_syscall_set_errno_tail
        jr      t9

        .cfi_endproc
END(safe_syscall_base)