aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <rth@twiddle.net>2010-05-03 10:07:55 -0700
committerAurelien Jarno <aurelien@aurel32.net>2010-05-21 16:22:21 +0000
commita5b3b13bedc545b5e571480b9cf47d2d378ad193 (patch)
treea452a2d96839feebd033e8ec8357257ab3b79d37
parentf43ce12b47323f0555ae9dfdf2f9d443346a4952 (diff)
downloadqemu-a5b3b13bedc545b5e571480b9cf47d2d378ad193.zip
qemu-a5b3b13bedc545b5e571480b9cf47d2d378ad193.tar.gz
qemu-a5b3b13bedc545b5e571480b9cf47d2d378ad193.tar.bz2
alpha-linux-user: Fix sigprocmask.
Alpha passes oldset by value in a register, and returns the newset as the return value; as compared to the standard implementation in which both are passed by reference. This requires being able to distinguish negative return values that are not errors. Do this in the same way as the Alpha Linux kernel, by storing a zero in V0 in the implementation of the syscall. At the same time, fix a think-o in the regular sigprocmask path in which we passed the target, rather than the host, HOW value. Signed-off-by: Richard Henderson <rth@twiddle.net> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
-rw-r--r--linux-user/main.c11
-rw-r--r--linux-user/syscall.c37
2 files changed, 43 insertions, 5 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index b158bbc..5027a6d 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -2499,8 +2499,15 @@ void cpu_loop (CPUState *env)
env->ir[IR_A0], env->ir[IR_A1],
env->ir[IR_A2], env->ir[IR_A3],
env->ir[IR_A4], env->ir[IR_A5]);
- if (trapnr != TARGET_NR_sigreturn
- && trapnr != TARGET_NR_rt_sigreturn) {
+ if (trapnr == TARGET_NR_sigreturn
+ || trapnr == TARGET_NR_rt_sigreturn) {
+ break;
+ }
+ /* Syscall writes 0 to V0 to bypass error check, similar
+ to how this is handled internal to Linux kernel. */
+ if (env->ir[IR_V0] == 0) {
+ env->ir[IR_V0] = sysret;
+ } else {
env->ir[IR_V0] = (sysret < 0 ? -sysret : sysret);
env->ir[IR_A3] = (sysret < 0);
}
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index fb18b24..bca8f70 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -4983,11 +4983,41 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
#ifdef TARGET_NR_sigprocmask
case TARGET_NR_sigprocmask:
{
- int how = arg1;
+#if defined(TARGET_ALPHA)
+ sigset_t set, oldset;
+ abi_ulong mask;
+ int how;
+
+ switch (arg1) {
+ case TARGET_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+ case TARGET_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+ case TARGET_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+ default:
+ ret = -TARGET_EINVAL;
+ goto fail;
+ }
+ mask = arg2;
+ target_to_host_old_sigset(&set, &mask);
+
+ ret = get_errno(sigprocmask(how, &set, &oldset));
+
+ if (!is_error(ret)) {
+ host_to_target_old_sigset(&mask, &oldset);
+ ret = mask;
+ ((CPUAlphaState *)cpu_env)->[IR_V0] = 0; /* force no error */
+ }
+#else
sigset_t set, oldset, *set_ptr;
+ int how;
if (arg2) {
- switch(how) {
+ switch (arg1) {
case TARGET_SIG_BLOCK:
how = SIG_BLOCK;
break;
@@ -5010,13 +5040,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
how = 0;
set_ptr = NULL;
}
- ret = get_errno(sigprocmask(arg1, set_ptr, &oldset));
+ ret = get_errno(sigprocmask(how, set_ptr, &oldset));
if (!is_error(ret) && arg3) {
if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0)))
goto efault;
host_to_target_old_sigset(p, &oldset);
unlock_user(p, arg3, sizeof(target_sigset_t));
}
+#endif
}
break;
#endif