blob: b283270391380c7c6ad311deb76935ef7087cc86 [file] [log] [blame]
/*
* Emulation of Linux signals
*
* Copyright (c) 2003 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/bitops.h"
#include <sys/ucontext.h>
#include <sys/resource.h>
#include "qemu.h"
#include "qemu-common.h"
#include "target_signal.h"
#include "trace.h"
static struct target_sigaltstack target_sigaltstack_used = {
.ss_sp = 0,
.ss_size = 0,
.ss_flags = TARGET_SS_DISABLE,
};
static struct target_sigaction sigact_table[TARGET_NSIG];
static void host_signal_handler(int host_signum, siginfo_t *info,
void *puc);
static uint8_t host_to_target_signal_table[_NSIG] = {
[SIGHUP] = TARGET_SIGHUP,
[SIGINT] = TARGET_SIGINT,
[SIGQUIT] = TARGET_SIGQUIT,
[SIGILL] = TARGET_SIGILL,
[SIGTRAP] = TARGET_SIGTRAP,
[SIGABRT] = TARGET_SIGABRT,
/* [SIGIOT] = TARGET_SIGIOT,*/
[SIGBUS] = TARGET_SIGBUS,
[SIGFPE] = TARGET_SIGFPE,
[SIGKILL] = TARGET_SIGKILL,
[SIGUSR1] = TARGET_SIGUSR1,
[SIGSEGV] = TARGET_SIGSEGV,
[SIGUSR2] = TARGET_SIGUSR2,
[SIGPIPE] = TARGET_SIGPIPE,
[SIGALRM] = TARGET_SIGALRM,
[SIGTERM] = TARGET_SIGTERM,
#ifdef SIGSTKFLT
[SIGSTKFLT] = TARGET_SIGSTKFLT,
#endif
[SIGCHLD] = TARGET_SIGCHLD,
[SIGCONT] = TARGET_SIGCONT,
[SIGSTOP] = TARGET_SIGSTOP,
[SIGTSTP] = TARGET_SIGTSTP,
[SIGTTIN] = TARGET_SIGTTIN,
[SIGTTOU] = TARGET_SIGTTOU,
[SIGURG] = TARGET_SIGURG,
[SIGXCPU] = TARGET_SIGXCPU,
[SIGXFSZ] = TARGET_SIGXFSZ,
[SIGVTALRM] = TARGET_SIGVTALRM,
[SIGPROF] = TARGET_SIGPROF,
[SIGWINCH] = TARGET_SIGWINCH,
[SIGIO] = TARGET_SIGIO,
[SIGPWR] = TARGET_SIGPWR,
[SIGSYS] = TARGET_SIGSYS,
/* next signals stay the same */
/* Nasty hack: Reverse SIGRTMIN and SIGRTMAX to avoid overlap with
host libpthread signals. This assumes no one actually uses SIGRTMAX :-/
To fix this properly we need to do manual signal delivery multiplexed
over a single host signal. */
[__SIGRTMIN] = __SIGRTMAX,
[__SIGRTMAX] = __SIGRTMIN,
};
static uint8_t target_to_host_signal_table[_NSIG];
static inline int on_sig_stack(unsigned long sp)
{
return (sp - target_sigaltstack_used.ss_sp
< target_sigaltstack_used.ss_size);
}
static inline int sas_ss_flags(unsigned long sp)
{
return (target_sigaltstack_used.ss_size == 0 ? SS_DISABLE
: on_sig_stack(sp) ? SS_ONSTACK : 0);
}
int host_to_target_signal(int sig)
{
if (sig < 0 || sig >= _NSIG)
return sig;
return host_to_target_signal_table[sig];
}
int target_to_host_signal(int sig)
{
if (sig < 0 || sig >= _NSIG)
return sig;
return target_to_host_signal_table[sig];
}
static inline void target_sigemptyset(target_sigset_t *set)
{
memset(set, 0, sizeof(*set));
}
static inline void target_sigaddset(target_sigset_t *set, int signum)
{
signum--;
abi_ulong mask = (abi_ulong)1 << (signum % TARGET_NSIG_BPW);
set->sig[signum / TARGET_NSIG_BPW] |= mask;
}
static inline int target_sigismember(const target_sigset_t *set, int signum)
{
signum--;
abi_ulong mask = (abi_ulong)1 << (signum % TARGET_NSIG_BPW);
return ((set->sig[signum / TARGET_NSIG_BPW] & mask) != 0);
}
static void host_to_target_sigset_internal(target_sigset_t *d,
const sigset_t *s)
{
int i;
target_sigemptyset(d);
for (i = 1; i <= TARGET_NSIG; i++) {
if (sigismember(s, i)) {
target_sigaddset(d, host_to_target_signal(i));
}
}
}
void host_to_target_sigset(target_sigset_t *d, const sigset_t *s)
{
target_sigset_t d1;
int i;
host_to_target_sigset_internal(&d1, s);
for(i = 0;i < TARGET_NSIG_WORDS; i++)
d->sig[i] = tswapal(d1.sig[i]);
}
static void target_to_host_sigset_internal(sigset_t *d,
const target_sigset_t *s)
{
int i;
sigemptyset(d);
for (i = 1; i <= TARGET_NSIG; i++) {
if (target_sigismember(s, i)) {
sigaddset(d, target_to_host_signal(i));
}
}
}
void target_to_host_sigset(sigset_t *d, const target_sigset_t *s)
{
target_sigset_t s1;
int i;
for(i = 0;i < TARGET_NSIG_WORDS; i++)
s1.sig[i] = tswapal(s->sig[i]);
target_to_host_sigset_internal(d, &s1);
}
void host_to_target_old_sigset(abi_ulong *old_sigset,
const sigset_t *sigset)
{
target_sigset_t d;
host_to_target_sigset(&d, sigset);
*old_sigset = d.sig[0];
}
void target_to_host_old_sigset(sigset_t *sigset,
const abi_ulong *old_sigset)
{
target_sigset_t d;
int i;
d.sig[0] = *old_sigset;
for(i = 1;i < TARGET_NSIG_WORDS; i++)
d.sig[i] = 0;
target_to_host_sigset(sigset, &d);
}
int block_signals(void)
{
TaskState *ts = (TaskState *)thread_cpu->opaque;
sigset_t set;
/* It's OK to block everything including SIGSEGV, because we won't
* run any further guest code before unblocking signals in
* process_pending_signals().
*/
sigfillset(&set);
sigprocmask(SIG_SETMASK, &set, 0);
return atomic_xchg(&ts->signal_pending, 1);
}
/* Wrapper for sigprocmask function
* Emulates a sigprocmask in a safe way for the guest. Note that set and oldset
* are host signal set, not guest ones. Returns -TARGET_ERESTARTSYS if
* a signal was already pending and the syscall must be restarted, or
* 0 on success.
* If set is NULL, this is guaranteed not to fail.
*/
int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
{
TaskState *ts = (TaskState *)thread_cpu->opaque;
if (oldset) {
*oldset = ts->signal_mask;
}
if (set) {
int i;
if (block_signals()) {
return -TARGET_ERESTARTSYS;
}
switch (how) {
case SIG_BLOCK:
sigorset(&ts->signal_mask, &ts->signal_mask, set);
break;
case SIG_UNBLOCK:
for (i = 1; i <= NSIG; ++i) {
if (sigismember(set, i)) {
sigdelset(&ts->signal_mask, i);
}
}
break;
case SIG_SETMASK:
ts->signal_mask = *set;
break;
default:
g_assert_not_reached();
}
/* Silently ignore attempts to change blocking status of KILL or STOP */
sigdelset(&ts->signal_mask, SIGKILL);
sigdelset(&ts->signal_mask, SIGSTOP);
}
return 0;
}
#if !defined(TARGET_OPENRISC) && !defined(TARGET_NIOS2)
/* Just set the guest's signal mask to the specified value; the
* caller is assumed to have called block_signals() already.
*/
static void set_sigmask(const sigset_t *set)
{
TaskState *ts = (TaskState *)thread_cpu->opaque;
ts->signal_mask = *set;
}
#endif
/* siginfo conversion */
static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo,
const siginfo_t *info)
{
int sig = host_to_target_signal(info->si_signo);
int si_code = info->si_code;
int si_type;
tinfo->si_signo = sig;
tinfo->si_errno = 0;
tinfo->si_code = info->si_code;
/* This memset serves two purposes:
* (1) ensure we don't leak random junk to the guest later
* (2) placate false positives from gcc about fields
* being used uninitialized if it chooses to inline both this
* function and tswap_siginfo() into host_to_target_siginfo().
*/
memset(tinfo->_sifields._pad, 0, sizeof(tinfo->_sifields._pad));
/* This is awkward, because we have to use a combination of
* the si_code and si_signo to figure out which of the union's
* members are valid. (Within the host kernel it is always possible
* to tell, but the kernel carefully avoids giving userspace the
* high 16 bits of si_code, so we don't have the information to
* do this the easy way...) We therefore make our best guess,
* bearing in mind that a guest can spoof most of the si_codes
* via rt_sigqueueinfo() if it likes.
*
* Once we have made our guess, we record it in the top 16 bits of
* the si_code, so that tswap_siginfo() later can use it.
* tswap_siginfo() will strip these top bits out before writing
* si_code to the guest (sign-extending the lower bits).
*/
switch (si_code) {
case SI_USER:
case SI_TKILL:
case SI_KERNEL:
/* Sent via kill(), tkill() or tgkill(), or direct from the kernel.
* These are the only unspoofable si_code values.
*/
tinfo->_sifields._kill._pid = info->si_pid;
tinfo->_sifields._kill._uid = info->si_uid;
si_type = QEMU_SI_KILL;
break;
default:
/* Everything else is spoofable. Make best guess based on signal */
switch (sig) {
case TARGET_SIGCHLD:
tinfo->_sifields._sigchld._pid = info->si_pid;
tinfo->_sifields._sigchld._uid = info->si_uid;
tinfo->_sifields._sigchld._status
= host_to_target_waitstatus(info->si_status);
tinfo->_sifields._sigchld._utime = info->si_utime;
tinfo->_sifields._sigchld._stime = info->si_stime;
si_type = QEMU_SI_CHLD;
break;
case TARGET_SIGIO:
tinfo->_sifields._sigpoll._band = info->si_band;
tinfo->_sifields._sigpoll._fd = info->si_fd;
si_type = QEMU_SI_POLL;
break;
default:
/* Assume a sigqueue()/mq_notify()/rt_sigqueueinfo() source. */
tinfo->_sifields._rt._pid = info->si_pid;
tinfo->_sifields._rt._uid = info->si_uid;
/* XXX: potential problem if 64 bit */
tinfo->_sifields._rt._sigval.sival_ptr
= (abi_ulong)(unsigned long)info->si_value.sival_ptr;
si_type = QEMU_SI_RT;
break;
}
break;
}
tinfo->si_code = deposit32(si_code, 16, 16, si_type);
}
static void tswap_siginfo(target_siginfo_t *tinfo,
const target_siginfo_t *info)
{
int si_type = extract32(info->si_code, 16, 16);
int si_code = sextract32(info->si_code, 0, 16);
__put_user(info->si_signo, &tinfo->si_signo);
__put_user(info->si_errno, &tinfo->si_errno);
__put_user(si_code, &tinfo->si_code);
/* We can use our internal marker of which fields in the structure
* are valid, rather than duplicating the guesswork of
* host_to_target_siginfo_noswap() here.
*/
switch (si_type) {
case QEMU_SI_KILL:
__put_user(info->_sifields._kill._pid, &tinfo->_sifields._kill._pid);
__put_user(info->_sifields._kill._uid, &tinfo->_sifields._kill._uid);
break;
case QEMU_SI_TIMER:
__put_user(info->_sifields._timer._timer1,
&tinfo->_sifields._timer._timer1);
__put_user(info->_sifields._timer._timer2,
&tinfo->_sifields._timer._timer2);
break;
case QEMU_SI_POLL:
__put_user(info->_sifields._sigpoll._band,
&tinfo->_sifields._sigpoll._band);
__put_user(info->_sifields._sigpoll._fd,
&tinfo->_sifields._sigpoll._fd);
break;
case QEMU_SI_FAULT:
__put_user(info->_sifields._sigfault._addr,
&tinfo->_sifields._sigfault._addr);
break;
case QEMU_SI_CHLD:
__put_user(info->_sifields._sigchld._pid,
&tinfo->_sifields._sigchld._pid);
__put_user(info->_sifields._sigchld._uid,
&tinfo->_sifields._sigchld._uid);
__put_user(info->_sifields._sigchld._status,
&tinfo->_sifields._sigchld._status);
__put_user(info->_sifields._sigchld._utime,
&tinfo->_sifields._sigchld._utime);
__put_user(info->_sifields._sigchld._stime,
&tinfo->_sifields._sigchld._stime);
break;
case QEMU_SI_RT:
__put_user(info->_sifields._rt._pid, &tinfo->_sifields._rt._pid);
__put_user(info->_sifields._rt._uid, &tinfo->_sifields._rt._uid);
__put_user(info->_sifields._rt._sigval.sival_ptr,
&tinfo->_sifields._rt._sigval.sival_ptr);
break;
default:
g_assert_not_reached();
}
}
void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info)
{
target_siginfo_t tgt_tmp;
host_to_target_siginfo_noswap(&tgt_tmp, info);
tswap_siginfo(tinfo, &tgt_tmp);
}
/* XXX: we support only POSIX RT signals are used. */
/* XXX: find a solution for 64 bit (additional malloced data is needed) */
void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo)
{
/* This conversion is used only for the rt_sigqueueinfo syscall,
* and so we know that the _rt fields are the valid ones.
*/
abi_ulong sival_ptr;
__get_user(info->si_signo, &tinfo->si_signo);
__get_user(info->si_errno, &tinfo->si_errno);
__get_user(info->si_code, &tinfo->si_code);
__get_user(info->si_pid, &tinfo->_sifields._rt._pid);
__get_user(info->si_uid, &tinfo->_sifields._rt._uid);
__get_user(sival_ptr, &tinfo->_sifields._rt._sigval.sival_ptr);
info->si_value.sival_ptr = (void *)(long)sival_ptr;
}
static int fatal_signal (int sig)
{
switch (sig) {
case TARGET_SIGCHLD:
case TARGET_SIGURG:
case TARGET_SIGWINCH:
/* Ignored by default. */
return 0;
case TARGET_SIGCONT:
case TARGET_SIGSTOP:
case TARGET_SIGTSTP:
case TARGET_SIGTTIN:
case TARGET_SIGTTOU:
/* Job control signals. */
return 0;
default:
return 1;
}
}
/* returns 1 if given signal should dump core if not handled */
static int core_dump_signal(int sig)
{
switch (sig) {
case TARGET_SIGABRT:
case TARGET_SIGFPE:
case TARGET_SIGILL:
case TARGET_SIGQUIT:
case TARGET_SIGSEGV:
case TARGET_SIGTRAP:
case TARGET_SIGBUS:
return (1);
default:
return (0);
}
}
void signal_init(void)
{
TaskState *ts = (TaskState *)thread_cpu->opaque;
struct sigaction act;
struct sigaction oact;
int i, j;
int host_sig;
/* generate signal conversion tables */
for(i = 1; i < _NSIG; i++) {
if (host_to_target_signal_table[i] == 0)
host_to_target_signal_table[i] = i;
}
for(i = 1; i < _NSIG; i++) {
j = host_to_target_signal_table[i];
target_to_host_signal_table[j] = i;
}
/* Set the signal mask from the host mask. */
sigprocmask(0, 0, &ts->signal_mask);
/* set all host signal handlers. ALL signals are blocked during
the handlers to serialize them. */
memset(sigact_table, 0, sizeof(sigact_table));
sigfillset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = host_signal_handler;
for(i = 1; i <= TARGET_NSIG; i++) {
host_sig = target_to_host_signal(i);
sigaction(host_sig, NULL, &oact);
if (oact.sa_sigaction == (void *)SIG_IGN) {
sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN;
} else if (oact.sa_sigaction == (void *)SIG_DFL) {
sigact_table[i - 1]._sa_handler = TARGET_SIG_DFL;
}
/* If there's already a handler installed then something has
gone horribly wrong, so don't even try to handle that case. */
/* Install some handlers for our own use. We need at least
SIGSEGV and SIGBUS, to detect exceptions. We can not just
trap all signals because it affects syscall interrupt
behavior. But do trap all default-fatal signals. */
if (fatal_signal (i))
sigaction(host_sig, &act, NULL);
}
}
/* Force a synchronously taken signal. The kernel force_sig() function
* also forces the signal to "not blocked, not ignored", but for QEMU
* that work is done in process_pending_signals().
*/
static void force_sig(int sig)
{
CPUState *cpu = thread_cpu;
CPUArchState *env = cpu->env_ptr;
target_siginfo_t info;
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_SI_KERNEL;
info._sifields._kill._pid = 0;
info._sifields._kill._uid = 0;
queue_signal(env, info.si_signo, QEMU_SI_KILL, &info);
}
/* Force a SIGSEGV if we couldn't write to memory trying to set
* up the signal frame. oldsig is the signal we were trying to handle
* at the point of failure.
*/
#if !defined(TARGET_RISCV)
static void force_sigsegv(int oldsig)
{
if (oldsig == SIGSEGV) {
/* Make sure we don't try to deliver the signal again; this will
* end up with handle_pending_signal() calling dump_core_and_abort().
*/
sigact_table[oldsig - 1]._sa_handler = TARGET_SIG_DFL;
}
force_sig(TARGET_SIGSEGV);
}
#endif
/* abort execution with signal */
static void QEMU_NORETURN dump_core_and_abort(int target_sig)
{
CPUState *cpu = thread_cpu;
CPUArchState *env = cpu->env_ptr;
TaskState *ts = (TaskState *)cpu->opaque;
int host_sig, core_dumped = 0;
struct sigaction act;
host_sig = target_to_host_signal(target_sig);
trace_user_force_sig(env, target_sig, host_sig);
gdb_signalled(env, target_sig);
/* dump core if supported by target binary format */
if (core_dump_signal(target_sig) && (ts->bprm->core_dump != NULL)) {
stop_all_tasks();
core_dumped =
((*ts->bprm->core_dump)(target_sig, env) == 0);
}
if (core_dumped) {
/* we already dumped the core of target process, we don't want
* a coredump of qemu itself */
struct rlimit nodump;
getrlimit(RLIMIT_CORE, &nodump);
nodump.rlim_cur=0;
setrlimit(RLIMIT_CORE, &nodump);
(void) fprintf(stderr, "qemu: uncaught target signal %d (%s) - %s\n",
target_sig, strsignal(host_sig), "core dumped" );
}
/* The proper exit code for dying from an uncaught signal is
* -<signal>. The kernel doesn't allow exit() or _exit() to pass
* a negative value. To get the proper exit code we need to
* actually die from an uncaught signal. Here the default signal
* handler is installed, we send ourself a signal and we wait for
* it to arrive. */
sigfillset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
sigaction(host_sig, &act, NULL);
/* For some reason raise(host_sig) doesn't send the signal when
* statically linked on x86-64. */
kill(getpid(), host_sig);
/* Make sure the signal isn't masked (just reuse the mask inside
of act) */
sigdelset(&act.sa_mask, host_sig);
sigsuspend(&act.sa_mask);
/* unreachable */
abort();
}
/* queue a signal so that it will be send to the virtual CPU as soon
as possible */
int queue_signal(CPUArchState *env, int sig, int si_type,
target_siginfo_t *info)
{
CPUState *cpu = ENV_GET_CPU(env);
TaskState *ts = cpu->opaque;
trace_user_queue_signal(env, sig);
info->si_code = deposit32(info->si_code, 16, 16, si_type);
ts->sync_signal.info = *info;
ts->sync_signal.pending = sig;
/* signal that a new signal is pending */
atomic_set(&ts->signal_pending, 1);
return 1; /* indicates that the signal was queued */
}
#ifndef HAVE_SAFE_SYSCALL
static inline void rewind_if_in_safe_syscall(void *puc)
{
/* Default version: never rewind */
}
#endif
static void host_signal_handler(int host_signum, siginfo_t *info,
void *puc)
{
CPUArchState *env = thread_cpu->env_ptr;
CPUState *cpu = ENV_GET_CPU(env);
TaskState *ts = cpu->opaque;
int sig;
target_siginfo_t tinfo;
ucontext_t *uc = puc;
struct emulated_sigtable *k;
/* the CPU emulator uses some host signals to detect exceptions,
we forward to it some signals */
if ((host_signum == SIGSEGV || host_signum == SIGBUS)
&& info->si_code > 0) {
if (cpu_signal_handler(host_signum, info, puc))
return;
}
/* get target signal number */
sig = host_to_target_signal(host_signum);
if (sig < 1 || sig > TARGET_NSIG)
return;
trace_user_host_signal(env, host_signum, sig);
rewind_if_in_safe_syscall(puc);
host_to_target_siginfo_noswap(&tinfo, info);
k = &ts->sigtab[sig - 1];
k->info = tinfo;
k->pending = sig;
ts->signal_pending = 1;
/* Block host signals until target signal handler entered. We
* can't block SIGSEGV or SIGBUS while we're executing guest
* code in case the guest code provokes one in the window between
* now and it getting out to the main loop. Signals will be
* unblocked again in process_pending_signals().
*
* WARNING: we cannot use sigfillset() here because the uc_sigmask
* field is a kernel sigset_t, which is much smaller than the
* libc sigset_t which sigfillset() operates on. Using sigfillset()
* would write 0xff bytes off the end of the structure and trash
* data on the struct.
* We can't use sizeof(uc->uc_sigmask) either, because the libc
* headers define the struct field with the wrong (too large) type.
*/
memset(&uc->uc_sigmask, 0xff, SIGSET_T_SIZE);
sigdelset(&uc->uc_sigmask, SIGSEGV);
sigdelset(&uc->uc_sigmask, SIGBUS);
/* interrupt the virtual CPU as soon as possible */
cpu_exit(thread_cpu);
}
/* do_sigaltstack() returns target values and errnos. */
/* compare linux/kernel/signal.c:do_sigaltstack() */
abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp)
{
int ret;
struct target_sigaltstack oss;
/* XXX: test errors */
if(uoss_addr)
{
__put_user(target_sigaltstack_used.ss_sp, &oss.ss_sp);
__put_user(target_sigaltstack_used.ss_size, &oss.ss_size);
__put_user(sas_ss_flags(sp), &oss.ss_flags);
}
if(uss_addr)
{
struct target_sigaltstack *uss;
struct target_sigaltstack ss;
size_t minstacksize = TARGET_MINSIGSTKSZ;
#if defined(TARGET_PPC64)
/* ELF V2 for PPC64 has a 4K minimum stack size for signal handlers */
struct image_info *image = ((TaskState *)thread_cpu->opaque)->info;
if (get_ppc64_abi(image) > 1) {
minstacksize = 4096;
}
#endif
ret = -TARGET_EFAULT;
if (!lock_user_struct(VERIFY_READ, uss, uss_addr, 1)) {
goto out;
}
__get_user(ss.ss_sp, &uss->ss_sp);
__get_user(ss.ss_size, &uss->ss_size);
__get_user(ss.ss_flags, &uss->ss_flags);
unlock_user_struct(uss, uss_addr, 0);
ret = -TARGET_EPERM;
if (on_sig_stack(sp))
goto out;
ret = -TARGET_EINVAL;
if (ss.ss_flags != TARGET_SS_DISABLE
&& ss.ss_flags != TARGET_SS_ONSTACK
&& ss.ss_flags != 0)
goto out;
if (ss.ss_flags == TARGET_SS_DISABLE) {
ss.ss_size = 0;
ss.ss_sp = 0;
} else {
ret = -TARGET_ENOMEM;
if (ss.ss_size < minstacksize) {
goto out;
}
}
target_sigaltstack_used.ss_sp = ss.ss_sp;
target_sigaltstack_used.ss_size = ss.ss_size;
}
if (uoss_addr) {
ret = -TARGET_EFAULT;
if (copy_to_user(uoss_addr, &oss, sizeof(oss)))
goto out;
}
ret = 0;
out:
return ret;
}
/* do_sigaction() return target values and host errnos */
int do_sigaction(int sig, const struct target_sigaction *act,
struct target_sigaction *oact)
{
struct target_sigaction *k;
struct sigaction act1;
int host_sig;
int ret = 0;
if (sig < 1 || sig > TARGET_NSIG || sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) {
return -TARGET_EINVAL;
}
if (block_signals()) {
return -TARGET_ERESTARTSYS;
}
k = &sigact_table[sig - 1];
if (oact) {
__put_user(k->_sa_handler, &oact->_sa_handler);
__put_user(k->sa_flags, &oact->sa_flags);
#ifdef TARGET_ARCH_HAS_SA_RESTORER
__put_user(k->sa_restorer, &oact->sa_restorer);
#endif
/* Not swapped. */
oact->sa_mask = k->sa_mask;
}
if (act) {
/* FIXME: This is not threadsafe. */
__get_user(k->_sa_handler, &act->_sa_handler);
__get_user(k->sa_flags, &act->sa_flags);
#ifdef TARGET_ARCH_HAS_SA_RESTORER
__get_user(k->sa_restorer, &act->sa_restorer);
#endif
/* To be swapped in target_to_host_sigset. */
k->sa_mask = act->sa_mask;
/* we update the host linux signal state */
host_sig = target_to_host_signal(sig);
if (host_sig != SIGSEGV && host_sig != SIGBUS) {
sigfillset(&act1.sa_mask);
act1.sa_flags = SA_SIGINFO;
if (k->sa_flags & TARGET_SA_RESTART)
act1.sa_flags |= SA_RESTART;
/* NOTE: it is important to update the host kernel signal
ignore state to avoid getting unexpected interrupted
syscalls */
if (k->_sa_handler == TARGET_SIG_IGN) {
act1.sa_sigaction = (void *)SIG_IGN;
} else if (k->_sa_handler == TARGET_SIG_DFL) {
if (fatal_signal (sig))
act1.sa_sigaction = host_signal_handler;
else
act1.sa_sigaction = (void *)SIG_DFL;
} else {
act1.sa_sigaction = host_signal_handler;
}
ret = sigaction(host_sig, &act1, NULL);
}
}
return ret;
}
#if defined(TARGET_I386)
/* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */
struct target_fpreg {
uint16_t significand[4];
uint16_t exponent;
};
struct target_fpxreg {
uint16_t significand[4];
uint16_t exponent;
uint16_t padding[3];
};
struct target_xmmreg {
uint32_t element[4];
};
struct target_fpstate_32 {
/* Regular FPU environment */
uint32_t cw;
uint32_t sw;
uint32_t tag;
uint32_t ipoff;
uint32_t cssel;
uint32_t dataoff;
uint32_t datasel;
struct target_fpreg st[8];
uint16_t status;
uint16_t magic; /* 0xffff = regular FPU data only */
/* FXSR FPU environment */
uint32_t _fxsr_env[6]; /* FXSR FPU env is ignored */
uint32_t mxcsr;
uint32_t reserved;
struct target_fpxreg fxsr_st[8]; /* FXSR FPU reg data is ignored */
struct target_xmmreg xmm[8];
uint32_t padding[56];
};
struct target_fpstate_64 {
/* FXSAVE format */
uint16_t cw;
uint16_t sw;
uint16_t twd;
uint16_t fop;
uint64_t rip;
uint64_t rdp;
uint32_t mxcsr;
uint32_t mxcsr_mask;
uint32_t st_space[32];
uint32_t xmm_space[64];
uint32_t reserved[24];
};
#ifndef TARGET_X86_64
# define target_fpstate target_fpstate_32
#else
# define target_fpstate target_fpstate_64
#endif
struct target_sigcontext_32 {
uint16_t gs, __gsh;
uint16_t fs, __fsh;
uint16_t es, __esh;
uint16_t ds, __dsh;
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t trapno;
uint32_t err;
uint32_t eip;
uint16_t cs, __csh;
uint32_t eflags;
uint32_t esp_at_signal;
uint16_t ss, __ssh;
uint32_t fpstate; /* pointer */
uint32_t oldmask;
uint32_t cr2;
};
struct target_sigcontext_64 {
uint64_t r8;
uint64_t r9;
uint64_t r10;
uint64_t r11;
uint64_t r12;
uint64_t r13;
uint64_t r14;
uint64_t r15;
uint64_t rdi;
uint64_t rsi;
uint64_t rbp;
uint64_t rbx;
uint64_t rdx;
uint64_t rax;
uint64_t rcx;
uint64_t rsp;
uint64_t rip;
uint64_t eflags;
uint16_t cs;
uint16_t gs;
uint16_t fs;
uint16_t ss;
uint64_t err;
uint64_t trapno;
uint64_t oldmask;
uint64_t cr2;
uint64_t fpstate; /* pointer */
uint64_t padding[8];
};
#ifndef TARGET_X86_64
# define target_sigcontext target_sigcontext_32
#else
# define target_sigcontext target_sigcontext_64
#endif
/* see Linux/include/uapi/asm-generic/ucontext.h */
struct target_ucontext {
abi_ulong tuc_flags;
abi_ulong tuc_link;
target_stack_t tuc_stack;
struct target_sigcontext tuc_mcontext;
target_sigset_t tuc_sigmask; /* mask last for extensibility */
};
#ifndef TARGET_X86_64
struct sigframe {
abi_ulong pretcode;
int sig;
struct target_sigcontext sc;
struct target_fpstate fpstate;
abi_ulong extramask[TARGET_NSIG_WORDS-1];
char retcode[8];
};
struct rt_sigframe {
abi_ulong pretcode;
int sig;
abi_ulong pinfo;
abi_ulong puc;
struct target_siginfo info;
struct target_ucontext uc;
struct target_fpstate fpstate;
char retcode[8];
};
#else
struct rt_sigframe {
abi_ulong pretcode;
struct target_ucontext uc;
struct target_siginfo info;
struct target_fpstate fpstate;
};
#endif
/*
* Set up a signal frame.
*/
/* XXX: save x87 state */
static void setup_sigcontext(struct target_sigcontext *sc,
struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask,
abi_ulong fpstate_addr)
{
CPUState *cs = CPU(x86_env_get_cpu(env));
#ifndef TARGET_X86_64
uint16_t magic;
/* already locked in setup_frame() */
__put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs);
__put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs);
__put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es);
__put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds);
__put_user(env->regs[R_EDI], &sc->edi);
__put_user(env->regs[R_ESI], &sc->esi);
__put_user(env->regs[R_EBP], &sc->ebp);
__put_user(env->regs[R_ESP], &sc->esp);
__put_user(env->regs[R_EBX], &sc->ebx);
__put_user(env->regs[R_EDX], &sc->edx);
__put_user(env->regs[R_ECX], &sc->ecx);
__put_user(env->regs[R_EAX], &sc->eax);
__put_user(cs->exception_index, &sc->trapno);
__put_user(env->error_code, &sc->err);
__put_user(env->eip, &sc->eip);
__put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs);
__put_user(env->eflags, &sc->eflags);
__put_user(env->regs[R_ESP], &sc->esp_at_signal);
__put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss);
cpu_x86_fsave(env, fpstate_addr, 1);
fpstate->status = fpstate->sw;
magic = 0xffff;
__put_user(magic, &fpstate->magic);
__put_user(fpstate_addr, &sc->fpstate);
/* non-iBCS2 extensions.. */
__put_user(mask, &sc->oldmask);
__put_user(env->cr[2], &sc->cr2);
#else
__put_user(env->regs[R_EDI], &sc->rdi);
__put_user(env->regs[R_ESI], &sc->rsi);
__put_user(env->regs[R_EBP], &sc->rbp);
__put_user(env->regs[R_ESP], &sc->rsp);
__put_user(env->regs[R_EBX], &sc->rbx);
__put_user(env->regs[R_EDX], &sc->rdx);
__put_user(env->regs[R_ECX], &sc->rcx);
__put_user(env->regs[R_EAX], &sc->rax);
__put_user(env->regs[8], &sc->r8);
__put_user(env->regs[9], &sc->r9);
__put_user(env->regs[10], &sc->r10);
__put_user(env->regs[11], &sc->r11);
__put_user(env->regs[12], &sc->r12);
__put_user(env->regs[13], &sc->r13);
__put_user(env->regs[14], &sc->r14);
__put_user(env->regs[15], &sc->r15);
__put_user(cs->exception_index, &sc->trapno);
__put_user(env->error_code, &sc->err);
__put_user(env->eip, &sc->rip);
__put_user(env->eflags, &sc->eflags);
__put_user(env->segs[R_CS].selector, &sc->cs);
__put_user((uint16_t)0, &sc->gs);
__put_user((uint16_t)0, &sc->fs);
__put_user(env->segs[R_SS].selector, &sc->ss);
__put_user(mask, &sc->oldmask);
__put_user(env->cr[2], &sc->cr2);
/* fpstate_addr must be 16 byte aligned for fxsave */
assert(!(fpstate_addr & 0xf));
cpu_x86_fxsave(env, fpstate_addr);
__put_user(fpstate_addr, &sc->fpstate);
#endif
}
/*
* Determine which stack to use..
*/
static inline abi_ulong
get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size)
{
unsigned long esp;
/* Default to using normal stack */
esp = env->regs[R_ESP];
#ifdef TARGET_X86_64
esp -= 128; /* this is the redzone */
#endif
/* This is the X/Open sanctioned signal stack switching. */
if (ka->sa_flags & TARGET_SA_ONSTACK) {
if (sas_ss_flags(esp) == 0) {
esp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
}
} else {
#ifndef TARGET_X86_64
/* This is the legacy signal stack switching. */
if ((env->segs[R_SS].selector & 0xffff) != __USER_DS &&
!(ka->sa_flags & TARGET_SA_RESTORER) &&
ka->sa_restorer) {
esp = (unsigned long) ka->sa_restorer;
}
#endif
}
#ifndef TARGET_X86_64
return (esp - frame_size) & -8ul;
#else
return ((esp - frame_size) & (~15ul)) - 8;
#endif
}
#ifndef TARGET_X86_64
/* compare linux/arch/i386/kernel/signal.c:setup_frame() */
static void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUX86State *env)
{
abi_ulong frame_addr;
struct sigframe *frame;
int i;
frame_addr = get_sigframe(ka, env, sizeof(*frame));
trace_user_setup_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
goto give_sigsegv;
__put_user(sig, &frame->sig);
setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0],
frame_addr + offsetof(struct sigframe, fpstate));
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
if (ka->sa_flags & TARGET_SA_RESTORER) {
__put_user(ka->sa_restorer, &frame->pretcode);
} else {
uint16_t val16;
abi_ulong retcode_addr;
retcode_addr = frame_addr + offsetof(struct sigframe, retcode);
__put_user(retcode_addr, &frame->pretcode);
/* This is popl %eax ; movl $,%eax ; int $0x80 */
val16 = 0xb858;
__put_user(val16, (uint16_t *)(frame->retcode+0));
__put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2));
val16 = 0x80cd;
__put_user(val16, (uint16_t *)(frame->retcode+6));
}
/* Set up registers for signal handler */
env->regs[R_ESP] = frame_addr;
env->eip = ka->_sa_handler;
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);
cpu_x86_load_seg(env, R_SS, __USER_DS);
cpu_x86_load_seg(env, R_CS, __USER_CS);
env->eflags &= ~TF_MASK;
unlock_user_struct(frame, frame_addr, 1);
return;
give_sigsegv:
force_sigsegv(sig);
}
#endif
/* compare linux/arch/x86/kernel/signal.c:setup_rt_frame() */
static void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUX86State *env)
{
abi_ulong frame_addr;
#ifndef TARGET_X86_64
abi_ulong addr;
#endif
struct rt_sigframe *frame;
int i;
frame_addr = get_sigframe(ka, env, sizeof(*frame));
trace_user_setup_rt_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
goto give_sigsegv;
/* These fields are only in rt_sigframe on 32 bit */
#ifndef TARGET_X86_64
__put_user(sig, &frame->sig);
addr = frame_addr + offsetof(struct rt_sigframe, info);
__put_user(addr, &frame->pinfo);
addr = frame_addr + offsetof(struct rt_sigframe, uc);
__put_user(addr, &frame->puc);
#endif
if (ka->sa_flags & TARGET_SA_SIGINFO) {
tswap_siginfo(&frame->info, info);
}
/* Create the ucontext. */
__put_user(0, &frame->uc.tuc_flags);
__put_user(0, &frame->uc.tuc_link);
__put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp);
__put_user(sas_ss_flags(get_sp_from_cpustate(env)),
&frame->uc.tuc_stack.ss_flags);
__put_user(target_sigaltstack_used.ss_size,
&frame->uc.tuc_stack.ss_size);
setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env,
set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate));
for(i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
#ifndef TARGET_X86_64
if (ka->sa_flags & TARGET_SA_RESTORER) {
__put_user(ka->sa_restorer, &frame->pretcode);
} else {
uint16_t val16;
addr = frame_addr + offsetof(struct rt_sigframe, retcode);
__put_user(addr, &frame->pretcode);
/* This is movl $,%eax ; int $0x80 */
__put_user(0xb8, (char *)(frame->retcode+0));
__put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1));
val16 = 0x80cd;
__put_user(val16, (uint16_t *)(frame->retcode+5));
}
#else
/* XXX: Would be slightly better to return -EFAULT here if test fails
assert(ka->sa_flags & TARGET_SA_RESTORER); */
__put_user(ka->sa_restorer, &frame->pretcode);
#endif
/* Set up registers for signal handler */
env->regs[R_ESP] = frame_addr;
env->eip = ka->_sa_handler;
#ifndef TARGET_X86_64
env->regs[R_EAX] = sig;
env->regs[R_EDX] = (unsigned long)&frame->info;
env->regs[R_ECX] = (unsigned long)&frame->uc;
#else
env->regs[R_EAX] = 0;
env->regs[R_EDI] = sig;
env->regs[R_ESI] = (unsigned long)&frame->info;
env->regs[R_EDX] = (unsigned long)&frame->uc;
#endif
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);
cpu_x86_load_seg(env, R_CS, __USER_CS);
cpu_x86_load_seg(env, R_SS, __USER_DS);
env->eflags &= ~TF_MASK;
unlock_user_struct(frame, frame_addr, 1);
return;
give_sigsegv:
force_sigsegv(sig);
}
static int
restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc)
{
unsigned int err = 0;
abi_ulong fpstate_addr;
unsigned int tmpflags;
#ifndef TARGET_X86_64
cpu_x86_load_seg(env, R_GS, tswap16(sc->gs));
cpu_x86_load_seg(env, R_FS, tswap16(sc->fs));
cpu_x86_load_seg(env, R_ES, tswap16(sc->es));
cpu_x86_load_seg(env, R_DS, tswap16(sc->ds));
env->regs[R_EDI] = tswapl(sc->edi);
env->regs[R_ESI] = tswapl(sc->esi);
env->regs[R_EBP] = tswapl(sc->ebp);
env->regs[R_ESP] = tswapl(sc->esp);
env->regs[R_EBX] = tswapl(sc->ebx);
env->regs[R_EDX] = tswapl(sc->edx);
env->regs[R_ECX] = tswapl(sc->ecx);
env->regs[R_EAX] = tswapl(sc->eax);
env->eip = tswapl(sc->eip);
#else
env->regs[8] = tswapl(sc->r8);
env->regs[9] = tswapl(sc->r9);
env->regs[10] = tswapl(sc->r10);
env->regs[11] = tswapl(sc->r11);
env->regs[12] = tswapl(sc->r12);
env->regs[13] = tswapl(sc->r13);
env->regs[14] = tswapl(sc->r14);
env->regs[15] = tswapl(sc->r15);
env->regs[R_EDI] = tswapl(sc->rdi);
env->regs[R_ESI] = tswapl(sc->rsi);
env->regs[R_EBP] = tswapl(sc->rbp);
env->regs[R_EBX] = tswapl(sc->rbx);
env->regs[R_EDX] = tswapl(sc->rdx);
env->regs[R_EAX] = tswapl(sc->rax);
env->regs[R_ECX] = tswapl(sc->rcx);
env->regs[R_ESP] = tswapl(sc->rsp);
env->eip = tswapl(sc->rip);
#endif
cpu_x86_load_seg(env, R_CS, lduw_p(&sc->cs) | 3);
cpu_x86_load_seg(env, R_SS, lduw_p(&sc->ss) | 3);
tmpflags = tswapl(sc->eflags);
env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5);
// regs->orig_eax = -1; /* disable syscall checks */
fpstate_addr = tswapl(sc->fpstate);
if (fpstate_addr != 0) {
if (!access_ok(VERIFY_READ, fpstate_addr,
sizeof(struct target_fpstate)))
goto badframe;
#ifndef TARGET_X86_64
cpu_x86_frstor(env, fpstate_addr, 1);
#else
cpu_x86_fxrstor(env, fpstate_addr);
#endif
}
return err;
badframe:
return 1;
}
/* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */
#ifndef TARGET_X86_64
long do_sigreturn(CPUX86State *env)
{
struct sigframe *frame;
abi_ulong frame_addr = env->regs[R_ESP] - 8;
target_sigset_t target_set;
sigset_t set;
int i;
trace_user_do_sigreturn(env, frame_addr);
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
goto badframe;
/* set blocked signals */
__get_user(target_set.sig[0], &frame->sc.oldmask);
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}
target_to_host_sigset_internal(&set, &target_set);
set_sigmask(&set);
/* restore registers */
if (restore_sigcontext(env, &frame->sc))
goto badframe;
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
#endif
long do_rt_sigreturn(CPUX86State *env)
{
abi_ulong frame_addr;
struct rt_sigframe *frame;
sigset_t set;
frame_addr = env->regs[R_ESP] - sizeof(abi_ulong);
trace_user_do_rt_sigreturn(env, frame_addr);
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
goto badframe;
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
set_sigmask(&set);
if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) {
goto badframe;
}
if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe, uc.tuc_stack), 0,
get_sp_from_cpustate(env)) == -EFAULT) {
goto badframe;
}
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
#elif defined(TARGET_AARCH64)
struct target_sigcontext {
uint64_t fault_address;
/* AArch64 registers */
uint64_t regs[31];
uint64_t sp;
uint64_t pc;
uint64_t pstate;
/* 4K reserved for FP/SIMD state and future expansion */
char __reserved[4096] __attribute__((__aligned__(16)));
};
struct target_ucontext {
abi_ulong tuc_flags;
abi_ulong tuc_link;
target_stack_t tuc_stack;
target_sigset_t tuc_sigmask;
/* glibc uses a 1024-bit sigset_t */
char __unused[1024 / 8 - sizeof(target_sigset_t)];
/* last for future expansion */
struct target_sigcontext tuc_mcontext;
};
/*
* Header to be used at the beginning of structures extending the user
* context. Such structures must be placed after the rt_sigframe on the stack
* and be 16-byte aligned. The last structure must be a dummy one with the
* magic and size set to 0.
*/
struct target_aarch64_ctx {
uint32_t magic;
uint32_t size;
};
#define TARGET_FPSIMD_MAGIC 0x46508001
struct target_fpsimd_context {
struct target_aarch64_ctx head;
uint32_t fpsr;
uint32_t fpcr;
uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */
};
#define TARGET_EXTRA_MAGIC 0x45585401
struct target_extra_context {
struct target_aarch64_ctx head;
uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */
uint32_t size; /* size in bytes of the extra space */
uint32_t reserved[3];
};
#define TARGET_SVE_MAGIC 0x53564501
struct target_sve_context {
struct target_aarch64_ctx head;
uint16_t vl;
uint16_t reserved[3];
/* The actual SVE data immediately follows. It is layed out
* according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of
* the original struct pointer.
*/
};
#define TARGET_SVE_VQ_BYTES 16
#define TARGET_SVE_SIG_ZREG_SIZE(VQ) ((VQ) * TARGET_SVE_VQ_BYTES)
#define TARGET_SVE_SIG_PREG_SIZE(VQ) ((VQ) * (TARGET_SVE_VQ_BYTES / 8))
#define TARGET_SVE_SIG_REGS_OFFSET \
QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES)
#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \
(TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N))
#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \
(TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N))
#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \
(TARGET_SVE_SIG_PREG_OFFSET(VQ, 16))
#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \
(TARGET_SVE_SIG_PREG_OFFSET(VQ, 17))
struct target_rt_sigframe {
struct target_siginfo info;
struct target_ucontext uc;
};
struct target_rt_frame_record {
uint64_t fp;
uint64_t lr;
uint32_t tramp[2];
};
static void target_setup_general_frame(struct target_rt_sigframe *sf,
CPUARMState *env, target_sigset_t *set)
{
int i;
__put_user(0, &sf->uc.tuc_flags);
__put_user(0, &sf->uc.tuc_link);
__put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp);
__put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags);
__put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size);
for (i = 0; i < 31; i++) {
__put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]);
}
__put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__put_user(env->pc, &sf->uc.tuc_mcontext.pc);
__put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate);
__put_user(env->exception.vaddress, &sf->uc.tuc_mcontext.fault_address);
for (i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]);
}
}
static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd,
CPUARMState *env)
{
int i;
__put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic);
__put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size);
__put_user(vfp_get_fpsr(env), &fpsimd->fpsr);
__put_user(vfp_get_fpcr(env), &fpsimd->fpcr);
for (i = 0; i < 32; i++) {
uint64_t *q = aa64_vfp_qreg(env, i);
#ifdef TARGET_WORDS_BIGENDIAN
__put_user(q[0], &fpsimd->vregs[i * 2 + 1]);
__put_user(q[1], &fpsimd->vregs[i * 2]);
#else
__put_user(q[0], &fpsimd->vregs[i * 2]);
__put_user(q[1], &fpsimd->vregs[i * 2 + 1]);
#endif
}
}
static void target_setup_extra_record(struct target_extra_context *extra,
uint64_t datap, uint32_t extra_size)
{
__put_user(TARGET_EXTRA_MAGIC, &extra->head.magic);
__put_user(sizeof(struct target_extra_context), &extra->head.size);
__put_user(datap, &extra->datap);
__put_user(extra_size, &extra->size);
}
static void target_setup_end_record(struct target_aarch64_ctx *end)
{
__put_user(0, &end->magic);
__put_user(0, &end->size);
}
static void target_setup_sve_record(struct target_sve_context *sve,
CPUARMState *env, int vq, int size)
{
int i, j;
__put_user(TARGET_SVE_MAGIC, &sve->head.magic);
__put_user(size, &sve->head.size);
__put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl);
/* Note that SVE regs are stored as a byte stream, with each byte element
* at a subsequent address. This corresponds to a little-endian store
* of our 64-bit hunks.
*/
for (i = 0; i < 32; ++i) {
uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
for (j = 0; j < vq * 2; ++j) {
__put_user_e(env->vfp.zregs[i].d[j], z + j, le);
}
}
for (i = 0; i <= 16; ++i) {
uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
for (j = 0; j < vq; ++j) {
uint64_t r = env->vfp.pregs[i].p[j >> 2];
__put_user_e(r >> ((j & 3) * 16), p + j, le);
}
}
}
static void target_restore_general_frame(CPUARMState *env,
struct target_rt_sigframe *sf)
{
sigset_t set;
uint64_t pstate;
int i;
target_to_host_sigset(&set, &sf->uc.tuc_sigmask);
set_sigmask(&set);
for (i = 0; i < 31; i++) {
__get_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]);
}
__get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp);
__get_user(env->pc, &sf->uc.tuc_mcontext.pc);
__get_user(pstate, &sf->uc.tuc_mcontext.pstate);
pstate_write(env, pstate);
}
static void target_restore_fpsimd_record(CPUARMState *env,
struct target_fpsimd_context *fpsimd)
{
uint32_t fpsr, fpcr;
int i;
__get_user(fpsr, &fpsimd->fpsr);
vfp_set_fpsr(env, fpsr);
__get_user(fpcr, &fpsimd->fpcr);
vfp_set_fpcr(env, fpcr);
for (i = 0; i < 32; i++) {
uint64_t *q = aa64_vfp_qreg(env, i);
#ifdef TARGET_WORDS_BIGENDIAN
__get_user(q[0], &fpsimd->vregs[i * 2 + 1]);
__get_user(q[1], &fpsimd->vregs[i * 2]);
#else
__get_user(q[0], &fpsimd->vregs[i * 2]);
__get_user(q[1], &fpsimd->vregs[i * 2 + 1]);
#endif
}
}
static void target_restore_sve_record(CPUARMState *env,
struct target_sve_context *sve, int vq)
{
int i, j;
/* Note that SVE regs are stored as a byte stream, with each byte element
* at a subsequent address. This corresponds to a little-endian load
* of our 64-bit hunks.
*/
for (i = 0; i < 32; ++i) {
uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i);
for (j = 0; j < vq * 2; ++j) {
__get_user_e(env->vfp.zregs[i].d[j], z + j, le);
}
}
for (i = 0; i <= 16; ++i) {
uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i);
for (j = 0; j < vq; ++j) {
uint16_t r;
__get_user_e(r, p + j, le);
if (j & 3) {
env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16);
} else {
env->vfp.pregs[i].p[j >> 2] = r;
}
}
}
}
static int target_restore_sigframe(CPUARMState *env,
struct target_rt_sigframe *sf)
{
struct target_aarch64_ctx *ctx, *extra = NULL;
struct target_fpsimd_context *fpsimd = NULL;
struct target_sve_context *sve = NULL;
uint64_t extra_datap = 0;
bool used_extra = false;
bool err = false;
int vq = 0, sve_size = 0;
target_restore_general_frame(env, sf);
ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved;
while (ctx) {
uint32_t magic, size, extra_size;
__get_user(magic, &ctx->magic);
__get_user(size, &ctx->size);
switch (magic) {
case 0:
if (size != 0) {
err = true;
goto exit;
}
if (used_extra) {
ctx = NULL;
} else {
ctx = extra;
used_extra = true;
}
continue;
case TARGET_FPSIMD_MAGIC:
if (fpsimd || size != sizeof(struct target_fpsimd_context)) {
err = true;
goto exit;
}
fpsimd = (struct target_fpsimd_context *)ctx;
break;
case TARGET_SVE_MAGIC:
if (arm_feature(env, ARM_FEATURE_SVE)) {
vq = (env->vfp.zcr_el[1] & 0xf) + 1;
sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
if (!sve && size == sve_size) {
sve = (struct target_sve_context *)ctx;
break;
}
}
err = true;
goto exit;
case TARGET_EXTRA_MAGIC:
if (extra || size != sizeof(struct target_extra_context)) {
err = true;
goto exit;
}
__get_user(extra_datap,
&((struct target_extra_context *)ctx)->datap);
__get_user(extra_size,
&((struct target_extra_context *)ctx)->size);
extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0);
break;
default:
/* Unknown record -- we certainly didn't generate it.
* Did we in fact get out of sync?
*/
err = true;
goto exit;
}
ctx = (void *)ctx + size;
}
/* Require FPSIMD always. */
if (fpsimd) {
target_restore_fpsimd_record(env, fpsimd);
} else {
err = true;
}
/* SVE data, if present, overwrites FPSIMD data. */
if (sve) {
target_restore_sve_record(env, sve, vq);
}
exit:
unlock_user(extra, extra_datap, 0);
return err;
}
static abi_ulong get_sigframe(struct target_sigaction *ka,
CPUARMState *env, int size)
{
abi_ulong sp;
sp = env->xregs[31];
/*
* This is the X/Open sanctioned signal stack switching.
*/
if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) {
sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
}
sp = (sp - size) & ~15;
return sp;
}
typedef struct {
int total_size;
int extra_base;
int extra_size;
int std_end_ofs;
int extra_ofs;
int extra_end_ofs;
} target_sigframe_layout;
static int alloc_sigframe_space(int this_size, target_sigframe_layout *l)
{
/* Make sure there will always be space for the end marker. */
const int std_size = sizeof(struct target_rt_sigframe)
- sizeof(struct target_aarch64_ctx);
int this_loc = l->total_size;
if (l->extra_base) {
/* Once we have begun an extra space, all allocations go there. */
l->extra_size += this_size;
} else if (this_size + this_loc > std_size) {
/* This allocation does not fit in the standard space. */
/* Allocate the extra record. */
l->extra_ofs = this_loc;
l->total_size += sizeof(struct target_extra_context);
/* Allocate the standard end record. */
l->std_end_ofs = l->total_size;
l->total_size += sizeof(struct target_aarch64_ctx);
/* Allocate the requested record. */
l->extra_base = this_loc = l->total_size;
l->extra_size = this_size;
}
l->total_size += this_size;
return this_loc;
}
static void target_setup_frame(int usig, struct target_sigaction *ka,
target_siginfo_t *info, target_sigset_t *set,
CPUARMState *env)
{
target_sigframe_layout layout = {
/* Begin with the size pointing to the reserved space. */
.total_size = offsetof(struct target_rt_sigframe,
uc.tuc_mcontext.__reserved),
};
int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0;
struct target_rt_sigframe *frame;
struct target_rt_frame_record *fr;
abi_ulong frame_addr, return_addr;
/* FPSIMD record is always in the standard space. */
fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context),
&layout);
/* SVE state needs saving only if it exists. */
if (arm_feature(env, ARM_FEATURE_SVE)) {
vq = (env->vfp.zcr_el[1] & 0xf) + 1;
sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
sve_ofs = alloc_sigframe_space(sve_size, &layout);
}
if (layout.extra_ofs) {
/* Reserve space for the extra end marker. The standard end marker
* will have been allocated when we allocated the extra record.
*/
layout.extra_end_ofs
= alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout);
} else {
/* Reserve space for the standard end marker.
* Do not use alloc_sigframe_space because we cheat
* std_size therein to reserve space for this.
*/
layout.std_end_ofs = layout.total_size;
layout.total_size += sizeof(struct target_aarch64_ctx);
}
/* We must always provide at least the standard 4K reserved space,
* even if we don't use all of it (this is part of the ABI)
*/
layout.total_size = MAX(layout.total_size,
sizeof(struct target_rt_sigframe));
/* Reserve space for the return code. On a real system this would
* be within the VDSO. So, despite the name this is not a "real"
* record within the frame.
*/
fr_ofs = layout.total_size;
layout.total_size += sizeof(struct target_rt_frame_record);
frame_addr = get_sigframe(ka, env, layout.total_size);
trace_user_setup_frame(env, frame_addr);
frame = lock_user(VERIFY_WRITE, frame_addr, layout.total_size, 0);
if (!frame) {
goto give_sigsegv;
}
target_setup_general_frame(frame, env, set);
target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env);
target_setup_end_record((void *)frame + layout.std_end_ofs);
if (layout.extra_ofs) {
target_setup_extra_record((void *)frame + layout.extra_ofs,
frame_addr + layout.extra_base,
layout.extra_size);
target_setup_end_record((void *)frame + layout.extra_end_ofs);
}
if (sve_ofs) {
target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size);
}
/* Set up the stack frame for unwinding. */
fr = (void *)frame + fr_ofs;
__put_user(env->xregs[29], &fr->fp);
__put_user(env->xregs[30], &fr->lr);
if (ka->sa_flags & TARGET_SA_RESTORER) {
return_addr = ka->sa_restorer;
} else {
/*
* mov x8,#__NR_rt_sigreturn; svc #0
* Since these are instructions they need to be put as little-endian
* regardless of target default or current CPU endianness.
*/
__put_user_e(0xd2801168, &fr->tramp[0], le);
__put_user_e(0xd4000001, &fr->tramp[1], le);
return_addr = frame_addr + fr_ofs
+ offsetof(struct target_rt_frame_record, tramp);
}
env->xregs[0] = usig;
env->xregs[31] = frame_addr;
env->xregs[29] = frame_addr + fr_ofs;
env->pc = ka->_sa_handler;
env->xregs[30] = return_addr;
if (info) {
tswap_siginfo(&frame->info, info);
env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info);
env->xregs[2] = frame_addr + offsetof(struct target_rt_sigframe, uc);
}
unlock_user(frame, frame_addr, layout.total_size);
return;
give_sigsegv:
unlock_user(frame, frame_addr, layout.total_size);
force_sigsegv(usig);
}
static void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info, target_sigset_t *set,
CPUARMState *env)
{
target_setup_frame(sig, ka, info, set, env);
}
static void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUARMState *env)
{
target_setup_frame(sig, ka, 0, set, env);
}
long do_rt_sigreturn(CPUARMState *env)
{
struct target_rt_sigframe *frame = NULL;
abi_ulong frame_addr = env->xregs[31];
trace_user_do_rt_sigreturn(env, frame_addr);
if (frame_addr & 15) {
goto badframe;
}
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
goto badframe;
}
if (target_restore_sigframe(env, frame)) {
goto badframe;
}
if (do_sigaltstack(frame_addr +
offsetof(struct target_rt_sigframe, uc.tuc_stack),
0, get_sp_from_cpustate(env)) == -EFAULT) {
goto badframe;
}
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
unlock_user_struct(frame, frame_addr, 0);
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
long do_sigreturn(CPUARMState *env)
{
return do_rt_sigreturn(env);
}
#elif defined(TARGET_ARM)
struct target_sigcontext {
abi_ulong trap_no;
abi_ulong error_code;
abi_ulong oldmask;
abi_ulong arm_r0;
abi_ulong arm_r1;
abi_ulong arm_r2;
abi_ulong arm_r3;
abi_ulong arm_r4;
abi_ulong arm_r5;
abi_ulong arm_r6;
abi_ulong arm_r7;
abi_ulong arm_r8;
abi_ulong arm_r9;
abi_ulong arm_r10;
abi_ulong arm_fp;
abi_ulong arm_ip;
abi_ulong arm_sp;
abi_ulong arm_lr;
abi_ulong arm_pc;
abi_ulong arm_cpsr;
abi_ulong fault_address;
};
struct target_ucontext_v1 {
abi_ulong tuc_flags;
abi_ulong tuc_link;
target_stack_t tuc_stack;
struct target_sigcontext tuc_mcontext;
target_sigset_t tuc_sigmask; /* mask last for extensibility */
};
struct target_ucontext_v2 {
abi_ulong tuc_flags;
abi_ulong tuc_link;
target_stack_t tuc_stack;
struct target_sigcontext tuc_mcontext;
target_sigset_t tuc_sigmask; /* mask last for extensibility */
char __unused[128 - sizeof(target_sigset_t)];
abi_ulong tuc_regspace[128] __attribute__((__aligned__(8)));
};
struct target_user_vfp {
uint64_t fpregs[32];
abi_ulong fpscr;
};
struct target_user_vfp_exc {
abi_ulong fpexc;
abi_ulong fpinst;
abi_ulong fpinst2;
};
struct target_vfp_sigframe {
abi_ulong magic;
abi_ulong size;
struct target_user_vfp ufp;
struct target_user_vfp_exc ufp_exc;
} __attribute__((__aligned__(8)));
struct target_iwmmxt_sigframe {
abi_ulong magic;
abi_ulong size;
uint64_t regs[16];
/* Note that not all the coprocessor control registers are stored here */
uint32_t wcssf;
uint32_t wcasf;
uint32_t wcgr0;
uint32_t wcgr1;
uint32_t wcgr2;
uint32_t wcgr3;
} __attribute__((__aligned__(8)));
#define TARGET_VFP_MAGIC 0x56465001
#define TARGET_IWMMXT_MAGIC 0x12ef842a
struct sigframe_v1
{
struct target_sigcontext sc;
abi_ulong extramask[TARGET_NSIG_WORDS-1];
abi_ulong retcode;
};
struct sigframe_v2
{
struct target_ucontext_v2 uc;
abi_ulong retcode;
};
struct rt_sigframe_v1
{
abi_ulong pinfo;
abi_ulong puc;
struct target_siginfo info;
struct target_ucontext_v1 uc;
abi_ulong retcode;
};
struct rt_sigframe_v2
{
struct target_siginfo info;
struct target_ucontext_v2 uc;
abi_ulong retcode;
};
#define TARGET_CONFIG_CPU_32 1
/*
* For ARM syscalls, we encode the syscall number into the instruction.
*/
#define SWI_SYS_SIGRETURN (0xef000000|(TARGET_NR_sigreturn + ARM_SYSCALL_BASE))
#define SWI_SYS_RT_SIGRETURN (0xef000000|(TARGET_NR_rt_sigreturn + ARM_SYSCALL_BASE))
/*
* For Thumb syscalls, we pass the syscall number via r7. We therefore
* need two 16-bit instructions.
*/
#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_sigreturn))
#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_rt_sigreturn))
static const abi_ulong retcodes[4] = {
SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN,
SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN
};
static inline int valid_user_regs(CPUARMState *regs)
{
return 1;
}
static void
setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/
CPUARMState *env, abi_ulong mask)
{
__put_user(env->regs[0], &sc->arm_r0);
__put_user(env->regs[1], &sc->arm_r1);
__put_user(env->regs[2], &sc->arm_r2);
__put_user(env->regs[3], &sc->arm_r3);
__put_user(env->regs[4], &sc->arm_r4);
__put_user(env->regs[5], &sc->arm_r5);
__put_user(env->regs[6], &sc->arm_r6);
__put_user(env->regs[7], &sc->arm_r7);
__put_user(env->regs[8], &sc->arm_r8);
__put_user(env->regs[9], &sc->arm_r9);
__put_user(env->regs[10], &sc->arm_r10);
__put_user(env->regs[11], &sc->arm_fp);
__put_user(env->regs[12], &sc->arm_ip);
__put_user(env->regs[13], &sc->arm_sp);
__put_user(env->regs[14], &sc->arm_lr);
__put_user(env->regs[15], &sc->arm_pc);
#ifdef TARGET_CONFIG_CPU_32
__put_user(cpsr_read(env), &sc->arm_cpsr);
#endif
__put_user(/* current->thread.trap_no */ 0, &sc->trap_no);
__put_user(/* current->thread.error_code */ 0, &sc->error_code);
__put_user(/* current->thread.address */ 0, &sc->fault_address);
__put_user(mask, &sc->oldmask);
}
static inline abi_ulong
get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize)
{
unsigned long sp = regs->regs[13];
/*
* This is the X/Open sanctioned signal stack switching.
*/
if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) {
sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
}
/*
* ATPCS B01 mandates 8-byte alignment
*/
return (sp - framesize) & ~7;
}
static void
setup_return(CPUARMState *env, struct target_sigaction *ka,
abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr)
{
abi_ulong handler = ka->_sa_handler;
abi_ulong retcode;
int thumb = handler & 1;
uint32_t cpsr = cpsr_read(env);
cpsr &= ~CPSR_IT;
if (thumb) {
cpsr |= CPSR_T;
} else {
cpsr &= ~CPSR_T;
}
if (ka->sa_flags & TARGET_SA_RESTORER) {
retcode = ka->sa_restorer;
} else {
unsigned int idx = thumb;
if (ka->sa_flags & TARGET_SA_SIGINFO) {
idx += 2;
}
__put_user(retcodes[idx], rc);
retcode = rc_addr + thumb;
}
env->regs[0] = usig;
env->regs[13] = frame_addr;
env->regs[14] = retcode;
env->regs[15] = handler & (thumb ? ~1 : ~3);
cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr);
}
static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env)
{
int i;
struct target_vfp_sigframe *vfpframe;
vfpframe = (struct target_vfp_sigframe *)regspace;
__put_user(TARGET_VFP_MAGIC, &vfpframe->magic);
__put_user(sizeof(*vfpframe), &vfpframe->size);
for (i = 0; i < 32; i++) {
__put_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]);
}
__put_user(vfp_get_fpscr(env), &vfpframe->ufp.fpscr);
__put_user(env->vfp.xregs[ARM_VFP_FPEXC], &vfpframe->ufp_exc.fpexc);
__put_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst);
__put_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2);
return (abi_ulong*)(vfpframe+1);
}
static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace,
CPUARMState *env)
{
int i;
struct target_iwmmxt_sigframe *iwmmxtframe;
iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace;
__put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic);
__put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size);
for (i = 0; i < 16; i++) {
__put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]);
}
__put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf);
__put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf);
__put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0);
__put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1);
__put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2);
__put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3);
return (abi_ulong*)(iwmmxtframe+1);
}
static void setup_sigframe_v2(struct target_ucontext_v2 *uc,
target_sigset_t *set, CPUARMState *env)
{
struct target_sigaltstack stack;
int i;
abi_ulong *regspace;
/* Clear all the bits of the ucontext we don't use. */
memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext));
memset(&stack, 0, sizeof(stack));
__put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp);
__put_user(target_sigaltstack_used.ss_size, &stack.ss_size);
__put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags);
memcpy(&uc->tuc_stack, &stack, sizeof(stack));
setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]);
/* Save coprocessor signal frame. */
regspace = uc->tuc_regspace;
if (arm_feature(env, ARM_FEATURE_VFP)) {
regspace = setup_sigframe_v2_vfp(regspace, env);
}
if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
regspace = setup_sigframe_v2_iwmmxt(regspace, env);
}
/* Write terminating magic word */
__put_user(0, regspace);
for(i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &uc->tuc_sigmask.sig[i]);
}
}
/* compare linux/arch/arm/kernel/signal.c:setup_frame() */
static void setup_frame_v1(int usig, struct target_sigaction *ka,
target_sigset_t *set, CPUARMState *regs)
{
struct sigframe_v1 *frame;
abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame));
int i;
trace_user_setup_frame(regs, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto sigsegv;
}
setup_sigcontext(&frame->sc, regs, set->sig[0]);
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}
setup_return(regs, ka, &frame->retcode, frame_addr, usig,
frame_addr + offsetof(struct sigframe_v1, retcode));
unlock_user_struct(frame, frame_addr, 1);
return;
sigsegv:
force_sigsegv(usig);
}
static void setup_frame_v2(int usig, struct target_sigaction *ka,
target_sigset_t *set, CPUARMState *regs)
{
struct sigframe_v2 *frame;
abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame));
trace_user_setup_frame(regs, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto sigsegv;
}
setup_sigframe_v2(&frame->uc, set, regs);
setup_return(regs, ka, &frame->retcode, frame_addr, usig,
frame_addr + offsetof(struct sigframe_v2, retcode));
unlock_user_struct(frame, frame_addr, 1);
return;
sigsegv:
force_sigsegv(usig);
}
static void setup_frame(int usig, struct target_sigaction *ka,
target_sigset_t *set, CPUARMState *regs)
{
if (get_osversion() >= 0x020612) {
setup_frame_v2(usig, ka, set, regs);
} else {
setup_frame_v1(usig, ka, set, regs);
}
}
/* compare linux/arch/arm/kernel/signal.c:setup_rt_frame() */
static void setup_rt_frame_v1(int usig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUARMState *env)
{
struct rt_sigframe_v1 *frame;
abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame));
struct target_sigaltstack stack;
int i;
abi_ulong info_addr, uc_addr;
trace_user_setup_rt_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto sigsegv;
}
info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info);
__put_user(info_addr, &frame->pinfo);
uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc);
__put_user(uc_addr, &frame->puc);
tswap_siginfo(&frame->info, info);
/* Clear all the bits of the ucontext we don't use. */
memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext));
memset(&stack, 0, sizeof(stack));
__put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp);
__put_user(target_sigaltstack_used.ss_size, &stack.ss_size);
__put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags);
memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack));
setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]);
for(i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}
setup_return(env, ka, &frame->retcode, frame_addr, usig,
frame_addr + offsetof(struct rt_sigframe_v1, retcode));
env->regs[1] = info_addr;
env->regs[2] = uc_addr;
unlock_user_struct(frame, frame_addr, 1);
return;
sigsegv:
force_sigsegv(usig);
}
static void setup_rt_frame_v2(int usig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUARMState *env)
{
struct rt_sigframe_v2 *frame;
abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame));
abi_ulong info_addr, uc_addr;
trace_user_setup_rt_frame(env, frame_addr);
if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
goto sigsegv;
}
info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info);
uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc);
tswap_siginfo(&frame->info, info);
setup_sigframe_v2(&frame->uc, set, env);
setup_return(env, ka, &frame->retcode, frame_addr, usig,
frame_addr + offsetof(struct rt_sigframe_v2, retcode));
env->regs[1] = info_addr;
env->regs[2] = uc_addr;
unlock_user_struct(frame, frame_addr, 1);
return;
sigsegv:
force_sigsegv(usig);
}
static void setup_rt_frame(int usig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUARMState *env)
{
if (get_osversion() >= 0x020612) {
setup_rt_frame_v2(usig, ka, info, set, env);
} else {
setup_rt_frame_v1(usig, ka, info, set, env);
}
}
static int
restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc)
{
int err = 0;
uint32_t cpsr;
__get_user(env->regs[0], &sc->arm_r0);
__get_user(env->regs[1], &sc->arm_r1);
__get_user(env->regs[2], &sc->arm_r2);
__get_user(env->regs[3], &sc->arm_r3);
__get_user(env->regs[4], &sc->arm_r4);
__get_user(env->regs[5], &sc->arm_r5);
__get_user(env->regs[6], &sc->arm_r6);
__get_user(env->regs[7], &sc->arm_r7);
__get_user(env->regs[8], &sc->arm_r8);
__get_user(env->regs[9], &sc->arm_r9);
__get_user(env->regs[10], &sc->arm_r10);
__get_user(env->regs[11], &sc->arm_fp);
__get_user(env->regs[12], &sc->arm_ip);
__get_user(env->regs[13], &sc->arm_sp);
__get_user(env->regs[14], &sc->arm_lr);
__get_user(env->regs[15], &sc->arm_pc);
#ifdef TARGET_CONFIG_CPU_32
__get_user(cpsr, &sc->arm_cpsr);
cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr);
#endif
err |= !valid_user_regs(env);
return err;
}
static long do_sigreturn_v1(CPUARMState *env)
{
abi_ulong frame_addr;
struct sigframe_v1 *frame = NULL;
target_sigset_t set;
sigset_t host_set;
int i;
/*
* Since we stacked the signal on a 64-bit boundary,
* then 'sp' should be word aligned here. If it's
* not, then the user is trying to mess with us.
*/
frame_addr = env->regs[13];
trace_user_do_sigreturn(env, frame_addr);
if (frame_addr & 7) {
goto badframe;
}
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
goto badframe;
}
__get_user(set.sig[0], &frame->sc.oldmask);
for(i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(set.sig[i], &frame->extramask[i - 1]);
}
target_to_host_sigset_internal(&host_set, &set);
set_sigmask(&host_set);
if (restore_sigcontext(env, &frame->sc)) {
goto badframe;
}
#if 0
/* Send SIGTRAP if we're single-stepping */
if (ptrace_cancel_bpt(current))
send_sig(SIGTRAP, current, 1);
#endif
unlock_user_struct(frame, frame_addr, 0);
return -TARGET_QEMU_ESIGRETURN;
badframe:
force_sig(TARGET_SIGSEGV);
return -TARGET_QEMU_ESIGRETURN;
}
static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace)
{
int i;
abi_ulong magic, sz;
uint32_t fpscr, fpexc;
struct target_vfp_sigframe *vfpframe;
vfpframe = (struct target_vfp_sigframe *)regspace;
__get_user(magic, &vfpframe->magic);
__get_user(sz, &vfpframe->size);
if (magic != TARGET_VFP_MAGIC || sz != sizeof(*vfpframe)) {
return 0;
}
for (i = 0; i < 32; i++) {
__get_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]);
}
__get_user(fpscr, &vfpframe->ufp.fpscr);
vfp_set_fpscr(env, fpscr);
__get_user(fpexc, &vfpframe->ufp_exc.fpexc);
/* Sanitise FPEXC: ensure VFP is enabled, FPINST2 is invalid
* and the exception flag is cleared
*/
fpexc |= (1 << 30);
fpexc &= ~((1 << 31) | (1 << 28));
env->vfp.xregs[ARM_VFP_FPEXC] = fpexc;
__get_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst);
__get_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2);
return (abi_ulong*)(vfpframe + 1);
}
static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env,
abi_ulong *regspace)
{
int i;
abi_ulong magic, sz;
struct target_iwmmxt_sigframe *iwmmxtframe;
iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace;
__get_user(magic, &iwmmxtframe->magic);
__get_user(sz, &iwmmxtframe->size);
if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) {
return 0;
}
for (i = 0; i < 16; i++) {
__get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]);
}
__get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf);
__get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf);
__get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0);
__get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1);
__get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2);
__get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3);
return (abi_ulong*)(iwmmxtframe + 1);
}
static int do_sigframe_return_v2(CPUARMState *env,
target_ulong context_addr,
struct target_ucontext_v2 *uc)
{
sigset_t host_set;
abi_ulong *regspace;
target_to_host_sigset(&host_set, &uc->tuc_sigmask);
set_sigmask(&host_set);
if (restore_sigcontext(env, &uc->tuc_mcontext))
return 1;
/* Restore coprocessor signal frame */
regspace = uc->tuc_regspace;
if (arm_feature(env, ARM_FEATURE_VFP)) {
regspace = restore_sigframe_v2_vfp(env, regspace);
if (!regspace) {
return 1;
}
}
if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
regspace = restore_sigframe_v2_iwmmxt(env, regspace);
if (!regspace) {
return 1;
}
}
if (do_sigaltstack(context_addr
+ offsetof(struct target_ucontext_v2, tuc_stack),
0, get_sp_from_cpustate(env)) == -EFAULT) {
return 1;
}
#if 0
/* Send SIGTRAP if we're single-stepping */
if (ptrace_cancel_bpt(current))
send_sig(SIGTRAP, current, 1);
#endif
return 0;
}
static long do_sigreturn_v2(CPUARMState *env)
{
abi_ulong frame_addr;
struct sigframe_v2 *frame = NULL;
/*
* Since we stacked the signal on a 64-bit boundary,
* then 'sp' should be word aligned here. If it's
* not, then the user is trying to mess with us.
*/
frame_addr = env->regs[13];
trace_user_do_sigreturn(env, frame_addr);
if (frame_addr & 7) {
goto badframe;
}
if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
goto badframe;
}
if (do_sigframe_return_v2(env,<