]>
Commit | Line | Data |
---|---|---|
4542adef RH |
1 | /* |
2 | * safe-syscall.inc.S : host-specific assembly fragment | |
3 | * to handle signals occurring at the same time as system calls. | |
4 | * This is intended to be included by linux-user/safe-syscall.S | |
5 | * | |
6 | * Written by Richard Henderson <richard.henderson@linaro.org> | |
7 | * Copyright (C) 2021 Linaro, Inc. | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | */ | |
12 | ||
13 | #include "sys/regdef.h" | |
14 | #include "sys/asm.h" | |
15 | ||
16 | .text | |
17 | .set nomips16 | |
18 | .set reorder | |
19 | ||
20 | .global safe_syscall_start | |
21 | .global safe_syscall_end | |
22 | .type safe_syscall_start, @function | |
23 | .type safe_syscall_end, @function | |
24 | ||
25 | /* | |
26 | * This is the entry point for making a system call. The calling | |
27 | * convention here is that of a C varargs function with the | |
28 | * first argument an 'int *' to the signal_pending flag, the | |
29 | * second one the system call number (as a 'long'), and all further | |
30 | * arguments being syscall arguments (also 'long'). | |
31 | */ | |
32 | ||
33 | #if _MIPS_SIM == _ABIO32 | |
34 | /* 8 * 4 = 32 for outgoing parameters; 1 * 4 for s0 save; 1 * 4 for align. */ | |
35 | #define FRAME 40 | |
36 | #define OFS_S0 32 | |
37 | #else | |
38 | /* 1 * 8 for s0 save; 1 * 8 for align. */ | |
39 | #define FRAME 16 | |
40 | #define OFS_S0 0 | |
41 | #endif | |
42 | ||
43 | ||
44 | NESTED(safe_syscall_base, FRAME, ra) | |
45 | .cfi_startproc | |
46 | PTR_ADDIU sp, sp, -FRAME | |
47 | .cfi_adjust_cfa_offset FRAME | |
48 | REG_S s0, OFS_S0(sp) | |
49 | .cfi_rel_offset s0, OFS_S0 | |
50 | #if _MIPS_SIM == _ABIO32 | |
51 | /* | |
52 | * The syscall calling convention is nearly the same as C: | |
53 | * we enter with a0 == &signal_pending | |
54 | * a1 == syscall number | |
55 | * a2, a3, stack == syscall arguments | |
56 | * and return the result in a0 | |
57 | * and the syscall instruction needs | |
58 | * v0 == syscall number | |
59 | * a0 ... a3, stack == syscall arguments | |
60 | * and returns the result in v0 | |
61 | * Shuffle everything around appropriately. | |
62 | */ | |
63 | move s0, a0 /* signal_pending pointer */ | |
64 | move v0, a1 /* syscall number */ | |
65 | move a0, a2 /* syscall arguments */ | |
66 | move a1, a3 | |
67 | lw a2, FRAME+16(sp) | |
68 | lw a3, FRAME+20(sp) | |
69 | lw t4, FRAME+24(sp) | |
70 | lw t5, FRAME+28(sp) | |
71 | lw t6, FRAME+32(sp) | |
72 | lw t7, FRAME+40(sp) | |
73 | sw t4, 16(sp) | |
74 | sw t5, 20(sp) | |
75 | sw t6, 24(sp) | |
76 | sw t7, 28(sp) | |
77 | #else | |
78 | /* | |
79 | * The syscall calling convention is nearly the same as C: | |
80 | * we enter with a0 == &signal_pending | |
81 | * a1 == syscall number | |
82 | * a2 ... a7 == syscall arguments | |
83 | * and return the result in a0 | |
84 | * and the syscall instruction needs | |
85 | * v0 == syscall number | |
86 | * a0 ... a5 == syscall arguments | |
87 | * and returns the result in v0 | |
88 | * Shuffle everything around appropriately. | |
89 | */ | |
90 | move s0, a0 /* signal_pending pointer */ | |
91 | move v0, a1 /* syscall number */ | |
92 | move a0, a2 /* syscall arguments */ | |
93 | move a1, a3 | |
94 | move a2, a4 | |
95 | move a3, a5 | |
96 | move a4, a6 | |
97 | move a5, a7 | |
98 | #endif | |
99 | ||
100 | /* | |
101 | * This next sequence of code works in conjunction with the | |
102 | * rewind_if_safe_syscall_function(). If a signal is taken | |
103 | * and the interrupted PC is anywhere between 'safe_syscall_start' | |
104 | * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. | |
105 | * The code sequence must therefore be able to cope with this, and | |
106 | * the syscall instruction must be the final one in the sequence. | |
107 | */ | |
108 | safe_syscall_start: | |
109 | /* If signal_pending is non-zero, don't do the call */ | |
110 | lw t1, 0(s0) | |
111 | bnez t1, 2f | |
112 | syscall | |
113 | safe_syscall_end: | |
114 | ||
115 | /* code path for having successfully executed the syscall */ | |
116 | REG_L s0, OFS_S0(sp) | |
117 | PTR_ADDIU sp, sp, FRAME | |
118 | .cfi_remember_state | |
119 | .cfi_adjust_cfa_offset -FRAME | |
120 | .cfi_restore s0 | |
121 | bnez a3, 1f | |
122 | jr ra | |
123 | .cfi_restore_state | |
124 | ||
125 | /* code path when we didn't execute the syscall */ | |
126 | 2: REG_L s0, OFS_S0(sp) | |
127 | PTR_ADDIU sp, sp, FRAME | |
128 | .cfi_adjust_cfa_offset -FRAME | |
129 | .cfi_restore s0 | |
af254a27 | 130 | li v0, QEMU_ERESTARTSYS |
4542adef RH |
131 | |
132 | /* code path setting errno */ | |
133 | /* | |
134 | * We didn't setup GP on entry, optimistic of the syscall success. | |
135 | * We must do so now to load the address of the helper, as required | |
136 | * by the ABI, into t9. | |
137 | * | |
138 | * Note that SETUP_GPX and SETUP_GPX64 are themselves conditional, | |
139 | * so we can simply let the one that's not empty succeed. | |
140 | */ | |
141 | 1: USE_ALT_CP(t0) | |
142 | SETUP_GPX(t1) | |
143 | SETUP_GPX64(t0, t1) | |
144 | PTR_LA t9, safe_syscall_set_errno_tail | |
145 | jr t9 | |
146 | ||
147 | .cfi_endproc | |
148 | END(safe_syscall_base) |