Import QEMU upstream snapshot d2e570c
Upstream: https://gitlab.com/qemu-project/qemu.git Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 <rth@twiddle.net>
|
||||
* Copyright (C) 2016 Red Hat, 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.
|
||||
*/
|
||||
|
||||
.global safe_syscall_base
|
||||
.global safe_syscall_start
|
||||
.global safe_syscall_end
|
||||
.type safe_syscall_base, #function
|
||||
.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').
|
||||
*/
|
||||
safe_syscall_base:
|
||||
.cfi_startproc
|
||||
/* The syscall calling convention isn't the same as the
|
||||
* C one:
|
||||
* we enter with x0 == &signal_pending
|
||||
* x1 == syscall number
|
||||
* x2 ... x7, (stack) == syscall arguments
|
||||
* and return the result in x0
|
||||
* and the syscall instruction needs
|
||||
* x8 == syscall number
|
||||
* x0 ... x6 == syscall arguments
|
||||
* and returns the result in x0
|
||||
* Shuffle everything around appropriately.
|
||||
*/
|
||||
mov x9, x0 /* signal_pending pointer */
|
||||
mov x8, x1 /* syscall number */
|
||||
mov x0, x2 /* syscall arguments */
|
||||
mov x1, x3
|
||||
mov x2, x4
|
||||
mov x3, x5
|
||||
mov x4, x6
|
||||
mov x5, x7
|
||||
ldr x6, [sp]
|
||||
|
||||
/* 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 */
|
||||
ldr w10, [x9]
|
||||
cbnz w10, 2f
|
||||
svc 0x0
|
||||
safe_syscall_end:
|
||||
|
||||
/* code path for having successfully executed the syscall */
|
||||
#if defined(__linux__)
|
||||
/* Linux kernel returns (small) negative errno. */
|
||||
cmp x0, #-4096
|
||||
b.hi 0f
|
||||
#elif defined(__FreeBSD__)
|
||||
/* FreeBSD kernel returns positive errno and C bit set. */
|
||||
b.cs 1f
|
||||
#else
|
||||
#error "unsupported os"
|
||||
#endif
|
||||
ret
|
||||
|
||||
#if defined(__linux__)
|
||||
/* code path setting errno */
|
||||
0: neg w0, w0
|
||||
b safe_syscall_set_errno_tail
|
||||
#endif
|
||||
|
||||
/* code path when we didn't execute the syscall */
|
||||
2: mov w0, #QEMU_ERESTARTSYS
|
||||
1: b safe_syscall_set_errno_tail
|
||||
|
||||
.cfi_endproc
|
||||
.size safe_syscall_base, .-safe_syscall_base
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Ported to LoongArch by WANG Xuerui <git@xen0n.name>
|
||||
*
|
||||
* Based on safe-syscall.inc.S code for RISC-V,
|
||||
* originally written by Richard Henderson <rth@twiddle.net>
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
.global safe_syscall_base
|
||||
.global safe_syscall_start
|
||||
.global safe_syscall_end
|
||||
.type safe_syscall_base, @function
|
||||
.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').
|
||||
*/
|
||||
safe_syscall_base:
|
||||
.cfi_startproc
|
||||
/*
|
||||
* 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
|
||||
* a7 == syscall number
|
||||
* a0 ... a5 == syscall arguments
|
||||
* and returns the result in a0
|
||||
* Shuffle everything around appropriately.
|
||||
*/
|
||||
move $t0, $a0 /* signal_pending pointer */
|
||||
move $t1, $a1 /* syscall number */
|
||||
move $a0, $a2 /* syscall arguments */
|
||||
move $a1, $a3
|
||||
move $a2, $a4
|
||||
move $a3, $a5
|
||||
move $a4, $a6
|
||||
move $a5, $a7
|
||||
move $a7, $t1
|
||||
|
||||
/*
|
||||
* We need to preserve the signal_pending pointer but t0 is
|
||||
* clobbered by syscalls on LoongArch, so we need to move it
|
||||
* somewhere else, ideally both preserved across syscalls and
|
||||
* clobbered by procedure calls so we don't have to allocate a
|
||||
* stack frame; a6 is just the register we want here.
|
||||
*/
|
||||
move $a6, $t0
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
ld.w $t1, $a6, 0
|
||||
bnez $t1, 2f
|
||||
syscall 0
|
||||
safe_syscall_end:
|
||||
/* code path for having successfully executed the syscall */
|
||||
li.w $t2, -4096
|
||||
bgtu $a0, $t2, 0f
|
||||
jr $ra
|
||||
|
||||
/* code path setting errno */
|
||||
0: sub.d $a0, $zero, $a0
|
||||
b safe_syscall_set_errno_tail
|
||||
|
||||
/* code path when we didn't execute the syscall */
|
||||
2: li.w $a0, QEMU_ERESTARTSYS
|
||||
b safe_syscall_set_errno_tail
|
||||
.cfi_endproc
|
||||
.size safe_syscall_base, .-safe_syscall_base
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 <rth@twiddle.net>
|
||||
* Copyright (C) 2016 Red Hat, 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.
|
||||
*/
|
||||
|
||||
.global safe_syscall_base
|
||||
.global safe_syscall_start
|
||||
.global safe_syscall_end
|
||||
.type safe_syscall_base, @function
|
||||
|
||||
.text
|
||||
|
||||
/* 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 _CALL_ELF == 2
|
||||
safe_syscall_base:
|
||||
.cfi_startproc
|
||||
.localentry safe_syscall_base,0
|
||||
#else
|
||||
.section ".opd","aw"
|
||||
.align 3
|
||||
safe_syscall_base:
|
||||
.quad .L.safe_syscall_base,.TOC.@tocbase,0
|
||||
.previous
|
||||
.L.safe_syscall_base:
|
||||
.cfi_startproc
|
||||
#endif
|
||||
/* We enter with r3 == &signal_pending
|
||||
* r4 == syscall number
|
||||
* r5 ... r10 == syscall arguments
|
||||
* and return the result in r3
|
||||
* and the syscall instruction needs
|
||||
* r0 == syscall number
|
||||
* r3 ... r8 == syscall arguments
|
||||
* and returns the result in r3
|
||||
* Shuffle everything around appropriately.
|
||||
*/
|
||||
std 14, 16(1) /* Preserve r14 in SP+16 */
|
||||
.cfi_offset 14, 16
|
||||
mr 14, 3 /* signal_pending */
|
||||
mr 0, 4 /* syscall number */
|
||||
mr 3, 5 /* syscall arguments */
|
||||
mr 4, 6
|
||||
mr 5, 7
|
||||
mr 6, 8
|
||||
mr 7, 9
|
||||
mr 8, 10
|
||||
|
||||
/* 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 */
|
||||
lwz 12, 0(14)
|
||||
cmpwi 0, 12, 0
|
||||
bne- 2f
|
||||
sc
|
||||
safe_syscall_end:
|
||||
/* code path when we did execute the syscall */
|
||||
ld 14, 16(1) /* restore r14 */
|
||||
bso- 1f
|
||||
blr
|
||||
|
||||
/* code path when we didn't execute the syscall */
|
||||
2: ld 14, 16(1) /* restore r14 */
|
||||
addi 3, 0, QEMU_ERESTARTSYS
|
||||
|
||||
/* code path setting errno */
|
||||
1: b safe_syscall_set_errno_tail
|
||||
nop /* per abi, for the linker to modify */
|
||||
|
||||
.cfi_endproc
|
||||
|
||||
#if _CALL_ELF == 2
|
||||
.size safe_syscall_base, .-safe_syscall_base
|
||||
#else
|
||||
.size safe_syscall_base, .-.L.safe_syscall_base
|
||||
.size .L.safe_syscall_base, .-.L.safe_syscall_base
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 <rth@twiddle.net>
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
.global safe_syscall_base
|
||||
.global safe_syscall_start
|
||||
.global safe_syscall_end
|
||||
.type safe_syscall_base, @function
|
||||
.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').
|
||||
*/
|
||||
safe_syscall_base:
|
||||
.cfi_startproc
|
||||
/*
|
||||
* 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
|
||||
* a7 == syscall number
|
||||
* a0 ... a5 == syscall arguments
|
||||
* and returns the result in a0
|
||||
* Shuffle everything around appropriately.
|
||||
*/
|
||||
mv t0, a0 /* signal_pending pointer */
|
||||
mv t1, a1 /* syscall number */
|
||||
mv a0, a2 /* syscall arguments */
|
||||
mv a1, a3
|
||||
mv a2, a4
|
||||
mv a3, a5
|
||||
mv a4, a6
|
||||
mv a5, a7
|
||||
mv a7, t1
|
||||
|
||||
/*
|
||||
* 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(t0)
|
||||
bnez t1, 2f
|
||||
scall
|
||||
safe_syscall_end:
|
||||
/* code path for having successfully executed the syscall */
|
||||
li t2, -4096
|
||||
bgtu a0, t2, 0f
|
||||
ret
|
||||
|
||||
/* code path setting errno */
|
||||
0: neg a0, a0
|
||||
tail safe_syscall_set_errno_tail
|
||||
|
||||
/* code path when we didn't execute the syscall */
|
||||
2: li a0, QEMU_ERESTARTSYS
|
||||
tail safe_syscall_set_errno_tail
|
||||
|
||||
.cfi_endproc
|
||||
.size safe_syscall_base, .-safe_syscall_base
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 <rth@twiddle.net>
|
||||
* Copyright (C) 2016 Red Hat, 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.
|
||||
*/
|
||||
|
||||
.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').
|
||||
*/
|
||||
safe_syscall_base:
|
||||
.cfi_startproc
|
||||
stmg %r6,%r15,48(%r15) /* save all call-saved registers */
|
||||
.cfi_offset %r15,-40
|
||||
.cfi_offset %r14,-48
|
||||
.cfi_offset %r13,-56
|
||||
.cfi_offset %r12,-64
|
||||
.cfi_offset %r11,-72
|
||||
.cfi_offset %r10,-80
|
||||
.cfi_offset %r9,-88
|
||||
.cfi_offset %r8,-96
|
||||
.cfi_offset %r7,-104
|
||||
.cfi_offset %r6,-112
|
||||
lgr %r1,%r15
|
||||
lg %r0,8(%r15) /* load eos */
|
||||
aghi %r15,-160
|
||||
.cfi_adjust_cfa_offset 160
|
||||
stg %r1,0(%r15) /* store back chain */
|
||||
stg %r0,8(%r15) /* store eos */
|
||||
|
||||
/*
|
||||
* The syscall calling convention isn't the same as the C one:
|
||||
* we enter with r2 == &signal_pending
|
||||
* r3 == syscall number
|
||||
* r4, r5, r6, (stack) == syscall arguments
|
||||
* and return the result in r2
|
||||
* and the syscall instruction needs
|
||||
* r1 == syscall number
|
||||
* r2 ... r7 == syscall arguments
|
||||
* and returns the result in r2
|
||||
* Shuffle everything around appropriately.
|
||||
*/
|
||||
lgr %r8,%r2 /* signal_pending pointer */
|
||||
lgr %r1,%r3 /* syscall number */
|
||||
lgr %r2,%r4 /* syscall args */
|
||||
lgr %r3,%r5
|
||||
lgr %r4,%r6
|
||||
lmg %r5,%r7,320(%r15)
|
||||
|
||||
/* 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 */
|
||||
icm %r0,15,0(%r8)
|
||||
jne 2f
|
||||
svc 0
|
||||
safe_syscall_end:
|
||||
|
||||
/* code path for having successfully executed the syscall */
|
||||
lg %r15,0(%r15) /* load back chain */
|
||||
.cfi_remember_state
|
||||
.cfi_adjust_cfa_offset -160
|
||||
lmg %r6,%r15,48(%r15) /* load saved registers */
|
||||
|
||||
lghi %r0, -4095 /* check for syscall error */
|
||||
clgr %r2, %r0
|
||||
blr %r14 /* return on success */
|
||||
lcr %r2, %r2 /* create positive errno */
|
||||
jg safe_syscall_set_errno_tail
|
||||
.cfi_restore_state
|
||||
|
||||
/* code path when we didn't execute the syscall */
|
||||
2: lg %r15,0(%r15) /* load back chain */
|
||||
.cfi_adjust_cfa_offset -160
|
||||
lmg %r6,%r15,48(%r15) /* load saved registers */
|
||||
lghi %r2, QEMU_ERESTARTSYS
|
||||
jg safe_syscall_set_errno_tail
|
||||
|
||||
.cfi_endproc
|
||||
.size safe_syscall_base, .-safe_syscall_base
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.text
|
||||
.balign 4
|
||||
|
||||
.register %g2, #scratch
|
||||
.register %g3, #scratch
|
||||
|
||||
.global safe_syscall_base
|
||||
.global safe_syscall_start
|
||||
.global safe_syscall_end
|
||||
.type safe_syscall_base, @function
|
||||
.type safe_syscall_start, @function
|
||||
.type safe_syscall_end, @function
|
||||
|
||||
#define STACK_BIAS 2047
|
||||
#define WINDOW_SIZE 16 * 8
|
||||
#define PARAM(N) STACK_BIAS + WINDOW_SIZE + N * 8
|
||||
|
||||
/*
|
||||
* 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').
|
||||
*/
|
||||
safe_syscall_base:
|
||||
.cfi_startproc
|
||||
/*
|
||||
* The syscall calling convention isn't the same as the C one:
|
||||
* we enter with o0 == &signal_pending
|
||||
* o1 == syscall number
|
||||
* o2 ... o5, (stack) == syscall arguments
|
||||
* and return the result in x0
|
||||
* and the syscall instruction needs
|
||||
* g1 == syscall number
|
||||
* o0 ... o5 == syscall arguments
|
||||
* and returns the result in o0
|
||||
* Shuffle everything around appropriately.
|
||||
*/
|
||||
mov %o0, %g2 /* signal_pending pointer */
|
||||
mov %o1, %g1 /* syscall number */
|
||||
mov %o2, %o0 /* syscall arguments */
|
||||
mov %o3, %o1
|
||||
mov %o4, %o2
|
||||
mov %o5, %o3
|
||||
ldx [%sp + PARAM(6)], %o4
|
||||
ldx [%sp + PARAM(7)], %o5
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
lduw [%g2], %g3
|
||||
brnz,pn %g3, 2f
|
||||
nop
|
||||
ta 0x6d
|
||||
safe_syscall_end:
|
||||
/* code path for having successfully executed the syscall */
|
||||
bcs,pn %xcc, 1f
|
||||
nop
|
||||
retl
|
||||
nop
|
||||
|
||||
/* code path when we didn't execute the syscall */
|
||||
2: set QEMU_ERESTARTSYS, %o0
|
||||
|
||||
/* code path setting errno */
|
||||
1: mov %o7, %g1
|
||||
call safe_syscall_set_errno_tail
|
||||
mov %g1, %o7
|
||||
|
||||
.cfi_endproc
|
||||
.size safe_syscall_base, .-safe_syscall_base
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Copyright (C) 2015 Timothy Edward Baldwin <T.E.Baldwin99@members.leeds.ac.uk>
|
||||
*
|
||||
* 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').
|
||||
*/
|
||||
safe_syscall_base:
|
||||
.cfi_startproc
|
||||
/* This saves a frame pointer and aligns the stack for the syscall.
|
||||
* (It's unclear if the syscall ABI has the same stack alignment
|
||||
* requirements as the userspace function call ABI, but better safe than
|
||||
* sorry. Appendix A2 of http://www.x86-64.org/documentation/abi.pdf
|
||||
* does not list any ABI differences regarding stack alignment.)
|
||||
*/
|
||||
push %rbp
|
||||
.cfi_adjust_cfa_offset 8
|
||||
.cfi_rel_offset rbp, 0
|
||||
|
||||
/*
|
||||
* The syscall calling convention isn't the same as the C one:
|
||||
* we enter with rdi == &signal_pending
|
||||
* rsi == syscall number
|
||||
* rdx, rcx, r8, r9, (stack), (stack) == syscall arguments
|
||||
* and return the result in rax
|
||||
* and the syscall instruction needs
|
||||
* rax == syscall number
|
||||
* rdi, rsi, rdx, r10, r8, r9 == syscall arguments
|
||||
* and returns the result in rax
|
||||
* Shuffle everything around appropriately.
|
||||
* Note that syscall will trash rcx and r11.
|
||||
*/
|
||||
mov %rsi, %rax /* syscall number */
|
||||
mov %rdi, %rbp /* signal_pending pointer */
|
||||
/* and the syscall arguments */
|
||||
mov %rdx, %rdi
|
||||
mov %rcx, %rsi
|
||||
mov %r8, %rdx
|
||||
mov %r9, %r10
|
||||
mov 16(%rsp), %r8
|
||||
mov 24(%rsp), %r9
|
||||
|
||||
/* 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 */
|
||||
cmpl $0, (%rbp)
|
||||
jnz 2f
|
||||
syscall
|
||||
safe_syscall_end:
|
||||
|
||||
/* code path for having successfully executed the syscall */
|
||||
#if defined(__linux__)
|
||||
/* Linux kernel returns (small) negative errno. */
|
||||
cmp $-4095, %rax
|
||||
jae 0f
|
||||
#elif defined(__FreeBSD__)
|
||||
/* FreeBSD kernel returns positive errno and C bit set. */
|
||||
jc 1f
|
||||
#else
|
||||
#error "unsupported os"
|
||||
#endif
|
||||
pop %rbp
|
||||
.cfi_remember_state
|
||||
.cfi_def_cfa_offset 8
|
||||
.cfi_restore rbp
|
||||
ret
|
||||
.cfi_restore_state
|
||||
|
||||
#if defined(__linux__)
|
||||
0: neg %eax
|
||||
jmp 1f
|
||||
#endif
|
||||
|
||||
/* code path when we didn't execute the syscall */
|
||||
2: mov $QEMU_ERESTARTSYS, %eax
|
||||
|
||||
/* code path setting errno */
|
||||
1: pop %rbp
|
||||
.cfi_def_cfa_offset 8
|
||||
.cfi_restore rbp
|
||||
mov %eax, %edi
|
||||
jmp safe_syscall_set_errno_tail
|
||||
.cfi_endproc
|
||||
|
||||
.size safe_syscall_base, .-safe_syscall_base
|
||||
@@ -0,0 +1,13 @@
|
||||
if not have_user
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
common_user_inc += include_directories('host/' / host_arch)
|
||||
|
||||
user_ss.add(files(
|
||||
'mmap-min-addr.c',
|
||||
'probe-guest-base.c',
|
||||
'safe-syscall.S',
|
||||
'safe-syscall-error.c',
|
||||
'selfmap.c',
|
||||
))
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Utility function to get the minimum mmap address.
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "user/mmap-min-addr.h"
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
uintptr_t mmap_min_addr;
|
||||
|
||||
static void __attribute__((constructor)) init(void)
|
||||
{
|
||||
#ifdef __linux__
|
||||
/*
|
||||
* We prefer to not make NULL pointers accessible to QEMU.
|
||||
* If something goes wrong below, fall back to 1 page.
|
||||
*/
|
||||
size_t min_addr = qemu_real_host_page_size();
|
||||
/*
|
||||
* Read in mmap_min_addr kernel parameter. This value is used
|
||||
* When loading the ELF image to determine whether guest_base
|
||||
* is needed. It is also used in mmap_find_vma.
|
||||
*/
|
||||
FILE *fp = fopen("/proc/sys/vm/mmap_min_addr", "r");
|
||||
|
||||
if (fp) {
|
||||
unsigned long tmp;
|
||||
if (fscanf(fp, "%lu", &tmp) == 1 && tmp != 0) {
|
||||
min_addr = MAX(min_addr, tmp);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
mmap_min_addr = min_addr;
|
||||
#elif defined(__FreeBSD__)
|
||||
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_VM_LAYOUT, getpid() };
|
||||
struct kinfo_vm_layout info;
|
||||
size_t info_len = sizeof(info);
|
||||
|
||||
mmap_min_addr =
|
||||
(sysctl(mib, ARRAY_SIZE(mib), &info, &info_len, NULL, 0) < 0
|
||||
? qemu_real_host_page_size()
|
||||
: info.kvm_min_user_addr);
|
||||
#else
|
||||
# error
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* QEMU Plugin API - *-user-mode only implementations
|
||||
*
|
||||
* Common user-mode only APIs are in plugins/api-user. These helpers
|
||||
* are only specific to the *-user frontends.
|
||||
*
|
||||
* Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
|
||||
* Copyright (C) 2019-2025, Linaro
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qemu/plugin.h"
|
||||
#include "accel/tcg/vcpu-state.h"
|
||||
#include "qemu.h"
|
||||
|
||||
/*
|
||||
* Binary path, start and end locations. Host specific due to TaskState.
|
||||
*/
|
||||
const char *qemu_plugin_path_to_binary(void)
|
||||
{
|
||||
TaskState *ts = get_task_state(current_cpu);
|
||||
return g_strdup(ts->bprm->filename);
|
||||
}
|
||||
|
||||
uint64_t qemu_plugin_start_code(void)
|
||||
{
|
||||
TaskState *ts = get_task_state(current_cpu);
|
||||
return ts->info->start_code;
|
||||
}
|
||||
|
||||
uint64_t qemu_plugin_end_code(void)
|
||||
{
|
||||
TaskState *ts = get_task_state(current_cpu);
|
||||
return ts->info->end_code;
|
||||
}
|
||||
|
||||
uint64_t qemu_plugin_entry_code(void)
|
||||
{
|
||||
TaskState *ts = get_task_state(current_cpu);
|
||||
return ts->info->entry;
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu/target-info.h"
|
||||
#include "qemu/log.h"
|
||||
#include "user/guest-base.h"
|
||||
#include "user/mmap-min-addr.h"
|
||||
#include "user/guest-base.h"
|
||||
#include "user/guest-host.h"
|
||||
#include "user/probe-guest-base.h"
|
||||
#include "user/selfmap.h"
|
||||
#include "exec/target_page.h"
|
||||
#include <sys/shm.h>
|
||||
|
||||
/* Linux and FreeBSD use different flags to express NOREPLACE. */
|
||||
#ifdef __FreeBSD__
|
||||
#define MAP_FIXED_NOREPLACE (MAP_FIXED | MAP_EXCL)
|
||||
#endif
|
||||
|
||||
uintptr_t guest_base;
|
||||
bool have_guest_base;
|
||||
|
||||
/**
|
||||
* pgb_try_mmap:
|
||||
* @addr: host start address
|
||||
* @addr_last: host last address
|
||||
* @keep: do not unmap the probe region
|
||||
*
|
||||
* Return 1 if [@addr, @addr_last] is not mapped in the host,
|
||||
* return 0 if it is not available to map, and -1 on mmap error.
|
||||
* If @keep, the region is left mapped on success, otherwise unmapped.
|
||||
*/
|
||||
static int pgb_try_mmap(uintptr_t addr, uintptr_t addr_last, bool keep)
|
||||
{
|
||||
size_t size = addr_last - addr + 1;
|
||||
void *p = mmap((void *)addr, size, PROT_NONE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE |
|
||||
MAP_NORESERVE | MAP_FIXED_NOREPLACE, -1, 0);
|
||||
int ret;
|
||||
|
||||
if (p == MAP_FAILED) {
|
||||
return errno == EEXIST ? 0 : -1;
|
||||
}
|
||||
ret = p == (void *)addr;
|
||||
if (!keep || !ret) {
|
||||
munmap(p, size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t size, uintptr_t brk)
|
||||
* @addr: host address
|
||||
* @addr_last: host last address
|
||||
* @brk: host brk
|
||||
*
|
||||
* Like pgb_try_mmap, but additionally reserve some memory following brk.
|
||||
*/
|
||||
static int pgb_try_mmap_skip_brk(uintptr_t addr, uintptr_t addr_last,
|
||||
uintptr_t brk, bool keep)
|
||||
{
|
||||
uintptr_t brk_last = brk + 16 * MiB - 1;
|
||||
|
||||
/* Do not map anything close to the host brk. */
|
||||
if (addr <= brk_last && brk <= addr_last) {
|
||||
return 0;
|
||||
}
|
||||
return pgb_try_mmap(addr, addr_last, keep);
|
||||
}
|
||||
|
||||
/**
|
||||
* pgb_try_mmap_set:
|
||||
* @ga: set of guest addrs
|
||||
* @base: guest_base
|
||||
* @brk: host brk
|
||||
*
|
||||
* Return true if all @ga can be mapped by the host at @base.
|
||||
* On success, retain the mapping at index 0 for reserved_va.
|
||||
*/
|
||||
|
||||
typedef struct PGBAddrs {
|
||||
PGBRange bounds[3];
|
||||
int nbounds;
|
||||
} PGBAddrs;
|
||||
|
||||
static bool pgb_try_mmap_set(const PGBAddrs *ga, uintptr_t base, uintptr_t brk)
|
||||
{
|
||||
for (int i = ga->nbounds - 1; i >= 0; --i) {
|
||||
if (pgb_try_mmap_skip_brk(ga->bounds[i].lo + base,
|
||||
ga->bounds[i].hi + base,
|
||||
brk, i == 0 && reserved_va) <= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* pgb_addr_set:
|
||||
* @ga: output set of guest addrs
|
||||
* @image_range: fixed guest image addresses
|
||||
* @identity: create for identity mapping
|
||||
*
|
||||
* Fill in @ga with the image, COMMPAGE and NULL page.
|
||||
*/
|
||||
static bool pgb_addr_set(PGBAddrs *ga, const PGBRange *image_range,
|
||||
const PGBRange *commpage_range, bool try_identity)
|
||||
{
|
||||
int n;
|
||||
|
||||
/*
|
||||
* With a low commpage, or a guest mapped very low,
|
||||
* we may not be able to use the identity map.
|
||||
*/
|
||||
if (try_identity) {
|
||||
if (commpage_range && commpage_range->lo < mmap_min_addr) {
|
||||
return false;
|
||||
}
|
||||
if (image_range && image_range->lo < mmap_min_addr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
memset(ga, 0, sizeof(*ga));
|
||||
n = 0;
|
||||
|
||||
if (reserved_va) {
|
||||
ga->bounds[n].lo = try_identity ? mmap_min_addr : 0;
|
||||
ga->bounds[n].hi = reserved_va;
|
||||
n++;
|
||||
/* Low COMMPAGE and NULL handled by reserving from 0. */
|
||||
} else {
|
||||
/* Add any low COMMPAGE or NULL page. */
|
||||
if (!try_identity || (commpage_range && commpage_range->lo == 0)) {
|
||||
ga->bounds[n].lo = 0;
|
||||
ga->bounds[n].hi = TARGET_PAGE_SIZE - 1;
|
||||
n++;
|
||||
}
|
||||
|
||||
/* Add the guest image for ET_EXEC. */
|
||||
if (image_range) {
|
||||
ga->bounds[n++] = *image_range;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add any high COMMPAGE not covered by reserved_va. */
|
||||
if (commpage_range && reserved_va < commpage_range->hi) {
|
||||
ga->bounds[n].lo = commpage_range->lo & qemu_real_host_page_mask();
|
||||
ga->bounds[n].hi = commpage_range->hi;
|
||||
n++;
|
||||
}
|
||||
|
||||
ga->nbounds = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void pgb_fail_in_use(const char *image_name)
|
||||
{
|
||||
error_report("%s: requires virtual address space that is in use "
|
||||
"(omit the -B option or choose a different value)",
|
||||
image_name);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void pgb_fixed(const char *image_name, const PGBRange *image_range,
|
||||
const PGBRange *commpage_range, uintptr_t align)
|
||||
{
|
||||
PGBAddrs ga;
|
||||
uintptr_t brk = (uintptr_t)sbrk(0);
|
||||
|
||||
if (!QEMU_IS_ALIGNED(guest_base, align)) {
|
||||
fprintf(stderr, "Requested guest base %p does not satisfy "
|
||||
"host minimum alignment (0x%" PRIxPTR ")\n",
|
||||
(void *)guest_base, align);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!pgb_addr_set(&ga, image_range, commpage_range, !guest_base)
|
||||
|| !pgb_try_mmap_set(&ga, guest_base, brk)) {
|
||||
pgb_fail_in_use(image_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pgb_find_fallback:
|
||||
*
|
||||
* This is a fallback method for finding holes in the host address space
|
||||
* if we don't have the benefit of being able to access /proc/self/map.
|
||||
* It can potentially take a very long time as we can only dumbly iterate
|
||||
* up the host address space seeing if the allocation would work.
|
||||
*/
|
||||
static uintptr_t pgb_find_fallback(const PGBAddrs *ga, uintptr_t align,
|
||||
uintptr_t brk)
|
||||
{
|
||||
/* TODO: come up with a better estimate of how much to skip. */
|
||||
uintptr_t skip = sizeof(uintptr_t) == 4 ? MiB : GiB;
|
||||
|
||||
for (uintptr_t base = skip; ; base += skip) {
|
||||
base = ROUND_UP(base, align);
|
||||
if (pgb_try_mmap_set(ga, base, brk)) {
|
||||
return base;
|
||||
}
|
||||
if (base >= -skip) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uintptr_t pgb_try_itree(const PGBAddrs *ga, uintptr_t base,
|
||||
IntervalTreeRoot *root)
|
||||
{
|
||||
for (int i = ga->nbounds - 1; i >= 0; --i) {
|
||||
uintptr_t s = base + ga->bounds[i].lo;
|
||||
uintptr_t l = base + ga->bounds[i].hi;
|
||||
IntervalTreeNode *n;
|
||||
|
||||
if (l < s) {
|
||||
/* Wraparound. Skip to advance S to mmap_min_addr. */
|
||||
return mmap_min_addr - s;
|
||||
}
|
||||
|
||||
n = interval_tree_iter_first(root, s, l);
|
||||
if (n != NULL) {
|
||||
/* Conflict. Skip to advance S to LAST + 1. */
|
||||
return n->last - s + 1;
|
||||
}
|
||||
}
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
static uintptr_t pgb_find_itree(const PGBAddrs *ga, IntervalTreeRoot *root,
|
||||
uintptr_t align, uintptr_t brk)
|
||||
{
|
||||
uintptr_t last = sizeof(uintptr_t) == 4 ? MiB : GiB;
|
||||
uintptr_t base, skip;
|
||||
|
||||
while (true) {
|
||||
base = ROUND_UP(last, align);
|
||||
if (base < last) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
skip = pgb_try_itree(ga, base, root);
|
||||
if (skip == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
last = base + skip;
|
||||
if (last < base) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We've chosen 'base' based on holes in the interval tree,
|
||||
* but we don't yet know if it is a valid host address.
|
||||
* Because it is the first matching hole, if the host addresses
|
||||
* are invalid we know there are no further matches.
|
||||
*/
|
||||
return pgb_try_mmap_set(ga, base, brk) ? base : -1;
|
||||
}
|
||||
|
||||
static void pgb_dynamic(const char *image_name, const PGBRange *image_range,
|
||||
const PGBRange *commpage_range, uintptr_t align)
|
||||
{
|
||||
IntervalTreeRoot *root;
|
||||
uintptr_t brk, ret;
|
||||
PGBAddrs ga;
|
||||
|
||||
/* Try the identity map first. */
|
||||
if (pgb_addr_set(&ga, image_range, commpage_range, true)) {
|
||||
brk = (uintptr_t)sbrk(0);
|
||||
if (pgb_try_mmap_set(&ga, 0, brk)) {
|
||||
guest_base = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Rebuild the address set for non-identity map.
|
||||
* This differs in the mapping of the guest NULL page.
|
||||
*/
|
||||
pgb_addr_set(&ga, image_range, commpage_range, false);
|
||||
|
||||
root = read_self_maps();
|
||||
|
||||
/* Read brk after we've read the maps, which will malloc. */
|
||||
brk = (uintptr_t)sbrk(0);
|
||||
|
||||
if (!root) {
|
||||
ret = pgb_find_fallback(&ga, align, brk);
|
||||
} else {
|
||||
/*
|
||||
* Reserve the area close to the host brk.
|
||||
* This will be freed with the rest of the tree.
|
||||
*/
|
||||
IntervalTreeNode *b = g_new0(IntervalTreeNode, 1);
|
||||
b->start = brk;
|
||||
b->last = brk + 16 * MiB - 1;
|
||||
interval_tree_insert(b, root);
|
||||
|
||||
ret = pgb_find_itree(&ga, root, align, brk);
|
||||
free_self_maps(root);
|
||||
}
|
||||
|
||||
if (ret == -1) {
|
||||
int w = target_long_bits() / 4;
|
||||
|
||||
error_report("%s: Unable to find a guest_base to satisfy all "
|
||||
"guest address mapping requirements", image_name);
|
||||
|
||||
for (int i = 0; i < ga.nbounds; ++i) {
|
||||
error_printf(" %0*" VADDR_PRIx "-%0*" VADDR_PRIx "\n",
|
||||
w, ga.bounds[i].lo,
|
||||
w, ga.bounds[i].hi);
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
guest_base = ret;
|
||||
}
|
||||
|
||||
void probe_guest_base(const char *image_name, const PGBRange *image_range,
|
||||
const PGBRange *commpage_range)
|
||||
{
|
||||
/* In order to use host shmat, we must be able to honor SHMLBA. */
|
||||
uintptr_t align = MAX(SHMLBA, TARGET_PAGE_SIZE);
|
||||
|
||||
/* Sanity check the guest binary. */
|
||||
if (reserved_va && image_range && image_range->hi > reserved_va) {
|
||||
error_report("%s: requires more than reserved virtual "
|
||||
"address space (0x%" VADDR_PRIx " > 0x%lx)",
|
||||
image_name, image_range->hi, reserved_va);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (have_guest_base) {
|
||||
pgb_fixed(image_name, image_range, commpage_range, align);
|
||||
} else {
|
||||
pgb_dynamic(image_name, image_range, commpage_range, align);
|
||||
}
|
||||
|
||||
assert(QEMU_IS_ALIGNED(guest_base, align));
|
||||
qemu_log_mask(CPU_LOG_PAGE, "Locating guest address space "
|
||||
"@ 0x%" PRIx64 "\n", (uint64_t)guest_base);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* safe-syscall-error.c: errno setting fragment
|
||||
* This is intended to be invoked by safe-syscall.S
|
||||
*
|
||||
* Written by Richard Henderson <[email protected]>
|
||||
* Copyright (C) 2021 Red Hat, 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 "qemu/osdep.h"
|
||||
#include "user/safe-syscall.h"
|
||||
|
||||
/*
|
||||
* This is intended to be invoked via tail-call on the error path
|
||||
* from the assembly in host/arch/safe-syscall.inc.S. This takes
|
||||
* care of the host specific addressing of errno.
|
||||
* Return -1 to finalize the return value for safe_syscall_base.
|
||||
*/
|
||||
long safe_syscall_set_errno_tail(int value)
|
||||
{
|
||||
errno = value;
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* safe-syscall.S : include the host-specific assembly fragment
|
||||
* to handle signals occurring at the same time as system calls.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "special-errno.h"
|
||||
|
||||
/* We have the correct host directory on our include path
|
||||
* so that this will pull in the right fragment for the architecture.
|
||||
*/
|
||||
#include "safe-syscall.inc.S"
|
||||
|
||||
/* We must specifically say that we're happy for the stack to not be
|
||||
* executable, otherwise the toolchain will default to assuming our
|
||||
* assembly needs an executable stack and the whole QEMU binary will
|
||||
* needlessly end up with one. This should be the last thing in this file.
|
||||
*/
|
||||
#if defined(__ELF__)
|
||||
.section .note.GNU-stack, "", %progbits
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Utility function to get QEMU's own process map
|
||||
*
|
||||
* Copyright (c) 2020 Linaro Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "user/selfmap.h"
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
IntervalTreeRoot *read_self_maps(void)
|
||||
{
|
||||
#ifdef __linux__
|
||||
IntervalTreeRoot *root;
|
||||
gchar *maps, **lines;
|
||||
guint i, nlines;
|
||||
|
||||
if (!g_file_get_contents("/proc/self/maps", &maps, NULL, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root = g_new0(IntervalTreeRoot, 1);
|
||||
lines = g_strsplit(maps, "\n", 0);
|
||||
nlines = g_strv_length(lines);
|
||||
|
||||
for (i = 0; i < nlines; i++) {
|
||||
gchar **fields = g_strsplit(lines[i], " ", 6);
|
||||
guint nfields = g_strv_length(fields);
|
||||
|
||||
if (nfields > 4) {
|
||||
uint64_t start, end, offset, inode;
|
||||
unsigned dev_maj, dev_min;
|
||||
int errors = 0;
|
||||
const char *p;
|
||||
|
||||
errors |= qemu_strtou64(fields[0], &p, 16, &start);
|
||||
errors |= qemu_strtou64(p + 1, NULL, 16, &end);
|
||||
errors |= qemu_strtou64(fields[2], NULL, 16, &offset);
|
||||
errors |= qemu_strtoui(fields[3], &p, 16, &dev_maj);
|
||||
errors |= qemu_strtoui(p + 1, NULL, 16, &dev_min);
|
||||
errors |= qemu_strtou64(fields[4], NULL, 10, &inode);
|
||||
|
||||
if (!errors) {
|
||||
size_t path_len;
|
||||
MapInfo *e;
|
||||
|
||||
if (nfields == 6) {
|
||||
p = fields[5];
|
||||
p += strspn(p, " ");
|
||||
path_len = strlen(p) + 1;
|
||||
} else {
|
||||
p = NULL;
|
||||
path_len = 0;
|
||||
}
|
||||
|
||||
e = g_malloc0(sizeof(*e) + path_len);
|
||||
|
||||
e->itree.start = start;
|
||||
e->itree.last = end - 1;
|
||||
e->offset = offset;
|
||||
e->dev = makedev(dev_maj, dev_min);
|
||||
e->inode = inode;
|
||||
|
||||
e->is_read = fields[1][0] == 'r';
|
||||
e->is_write = fields[1][1] == 'w';
|
||||
e->is_exec = fields[1][2] == 'x';
|
||||
e->is_priv = fields[1][3] == 'p';
|
||||
|
||||
if (path_len) {
|
||||
e->path = memcpy(e + 1, p, path_len);
|
||||
}
|
||||
|
||||
interval_tree_insert(&e->itree, root);
|
||||
}
|
||||
}
|
||||
g_strfreev(fields);
|
||||
}
|
||||
g_strfreev(lines);
|
||||
g_free(maps);
|
||||
|
||||
return root;
|
||||
#elif defined(__FreeBSD__)
|
||||
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
|
||||
size_t len = 0;
|
||||
g_autofree void *buf = NULL;
|
||||
IntervalTreeRoot *root;
|
||||
|
||||
/* Probe for buffer size. */
|
||||
if (sysctl(mib, ARRAY_SIZE(mib), NULL, &len, NULL, 0) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = g_malloc(len);
|
||||
if (sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root = g_new0(IntervalTreeRoot, 1);
|
||||
|
||||
for (size_t i = 0; i < len; ) {
|
||||
struct kinfo_vmentry *k = buf + i;
|
||||
MapInfo *e = g_new0(MapInfo, 1);
|
||||
|
||||
e->itree.start = k->kve_start;
|
||||
e->itree.last = k->kve_end - 1;
|
||||
|
||||
/*
|
||||
* TODO: The rest of the fields in MapInfo are used by linux-user
|
||||
* for the implementation of open_self_maps(). These fields are
|
||||
* quite specific to the textual format of /proc/self/maps.
|
||||
*
|
||||
* We may need something different to emulate KERN_PROC_VMMAP
|
||||
* in bsd-user, but so far they're unused -- leave them zeroed.
|
||||
*/
|
||||
|
||||
interval_tree_insert(&e->itree, root);
|
||||
i += k->kve_structsize;
|
||||
}
|
||||
|
||||
return root;
|
||||
#else
|
||||
# error
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* free_self_maps:
|
||||
* @root: an interval tree
|
||||
*
|
||||
* Free a tree of MapInfo structures.
|
||||
* Since we allocated each MapInfo in one chunk, we need not consider the
|
||||
* contents and can simply free each RBNode.
|
||||
*/
|
||||
|
||||
static void free_rbnode(RBNode *n)
|
||||
{
|
||||
if (n) {
|
||||
free_rbnode(n->rb_left);
|
||||
free_rbnode(n->rb_right);
|
||||
g_free(n);
|
||||
}
|
||||
}
|
||||
|
||||
void free_self_maps(IntervalTreeRoot *root)
|
||||
{
|
||||
if (root) {
|
||||
free_rbnode(root->rb_root.rb_node);
|
||||
g_free(root);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user