aboutsummaryrefslogtreecommitdiff
path: root/linux-user/host/i386/safe-syscall.inc.S
blob: 9d983321486598e0a4a555f16074a302fb618e8a (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
/*
 * 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 linux-user/safe-syscall.S
 *
 *  * Written by Peter Maydell <peter.maydell@linaro.org>
 *
 * Copyright (C) 2016 Linaro Limited
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
 */

    .global safe_syscall_base
    .global safe_syscall_start
    .global safe_syscall_end
    .type   safe_syscall_base, @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').
     * We return a long which is the syscall's return value, which
     * may be negative-errno on failure. Conversion to the
     * -1-and-errno-set convention is done by the calling wrapper.
     */
safe_syscall_base:
    .cfi_startproc
    /* Save ebx esi edi ebp, as the calling convention requires */
    pushl %ebp
    .cfi_adjust_cfa_offset 4
    .cfi_rel_offset ebp, 0
    pushl %edi
    .cfi_adjust_cfa_offset 4
    .cfi_rel_offset edi, 0
    pushl %esi
    .cfi_adjust_cfa_offset 4
    .cfi_rel_offset esi, 0
    pushl %ebx
    .cfi_adjust_cfa_offset 4
    .cfi_rel_offset ebx, 0

    /* The syscall calling convention isn't the same as the
     * C one:
     * we enter with all arguments on the stack:
     *           *signal_pending
     *           syscall number
     *           syscall arguments
     *           and return the result in eax
     * and the syscall instruction needs
     *           eax == syscall number
     *           ebx, ecx, edx, esi, edi, ebp == syscall arguments
     *           and returns the result in eax
     * Shuffle everything around appropriately.
     *
     * At this point the stack layout is:
     * 48(%esp)     syscall arg 6
     * 44(%esp)     syscall arg 5
     * 40(%esp)     syscall arg 4
     * 36(%esp)     syscall arg 3
     * 32(%esp)     syscall arg 2
     * 28(%esp)     syscall arg 1
     * 24(%esp)     syscall number
     * 20(%esp)     *signal_pending
     * 16(%esp)     return address
     * 12(%esp)     saved EBP
     *  8(%esp)     saved EDI
     *  4(%esp)     saved ESI
     *   (%esp)     saved EBX
     */
    /* syscall arguments */
    movl 48(%esp), %ebp
    movl 44(%esp), %edi
    movl 40(%esp), %esi
    movl 36(%esp), %edx
    movl 32(%esp), %ecx
    movl 28(%esp), %ebx

    /* 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.
     * For i386 we have absolutely no spare registers at the point of
     * the syscall, so we must load *signal_pending and the syscall
     * number off the stack inside the start/end section.
     */
safe_syscall_start:
    /* *signal_pending */
    movl 20(%esp), %eax
    /* if signal_pending is non-zero, don't do the call */
    testl $1, (%eax)
    jnz return_ERESTARTSYS
    movl 24(%esp), %eax
    int $0x80
safe_syscall_end:
    /* code path for having successfully executed the syscall */
    .cfi_remember_state
    popl %ebx
    .cfi_adjust_cfa_offset -4
    .cfi_restore ebx
    popl %esi
    .cfi_adjust_cfa_offset -4
    .cfi_restore esi
    popl %edi
    .cfi_adjust_cfa_offset -4
    .cfi_restore edi
    popl %ebp
    .cfi_adjust_cfa_offset -4
    .cfi_restore ebp
    ret

return_ERESTARTSYS:
    /* code path when we didn't execute the syscall */
    .cfi_restore_state
    mov $-TARGET_ERESTARTSYS, %eax
    popl %ebx
    .cfi_adjust_cfa_offset -4
    .cfi_restore ebx
    popl %esi
    .cfi_adjust_cfa_offset -4
    .cfi_restore esi
    popl %edi
    .cfi_adjust_cfa_offset -4
    .cfi_restore edi
    popl %ebp
    .cfi_adjust_cfa_offset -4
    .cfi_restore ebp
    ret
    .cfi_endproc

    .size   safe_syscall_base, .-safe_syscall_base