From 2466119c9551d606a0f92f9832e0c865bc04b488 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:25 +0100 Subject: linux-user: Check array bounds in errno conversion Check array bounds in host_to_target_errno() and target_to_host_errno(). Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-2-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Add a lower-bound check, use braces on if(), tweak commit message] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio Reviewed-by: Laurent Vivier --- linux-user/syscall.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 032d338..5246f36 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -619,15 +619,19 @@ static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = { static inline int host_to_target_errno(int err) { - if(host_to_target_errno_table[err]) + if (err >= 0 && err < ERRNO_TABLE_SIZE && + host_to_target_errno_table[err]) { return host_to_target_errno_table[err]; + } return err; } static inline int target_to_host_errno(int err) { - if (target_to_host_errno_table[err]) + if (err >= 0 && err < ERRNO_TABLE_SIZE && + target_to_host_errno_table[err]) { return target_to_host_errno_table[err]; + } return err; } -- cgit v1.1 From a3ca7bb2592010eedca31abd11d3ab451cf3b738 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 May 2016 18:47:26 +0100 Subject: linux-user: Consistently return host errnos from do_openat() The function do_openat() is not consistent about whether it is returning a host errno or a guest errno in case of failure. Standardise on returning -1 with errno set (ie caller has to call get_errno()). Signed-off-by: Peter Maydell Reported-by: Timothy Edward Baldwin Signed-off-by: Riku Voipio Reviewed-by: Laurent Vivier --- linux-user/syscall.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 5246f36..f4c2e19 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5559,7 +5559,9 @@ static int open_self_cmdline(void *cpu_env, int fd) nb_read = read(fd_orig, buf, sizeof(buf)); if (nb_read < 0) { + int e = errno; fd_orig = close(fd_orig); + errno = e; return -1; } else if (nb_read == 0) { break; @@ -5579,7 +5581,9 @@ static int open_self_cmdline(void *cpu_env, int fd) if (word_skipped) { if (write(fd, cp_buf, nb_read) != nb_read) { + int e = errno; close(fd_orig); + errno = e; return -1; } } @@ -5599,7 +5603,7 @@ static int open_self_maps(void *cpu_env, int fd) fp = fopen("/proc/self/maps", "r"); if (fp == NULL) { - return -EACCES; + return -1; } while ((read = getline(&line, &len, fp)) != -1) { @@ -5743,7 +5747,7 @@ static int open_net_route(void *cpu_env, int fd) fp = fopen("/proc/net/route", "r"); if (fp == NULL) { - return -EACCES; + return -1; } /* read header */ @@ -5793,7 +5797,7 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, if (is_proc_myself(pathname, "exe")) { int execfd = qemu_getauxval(AT_EXECFD); - return execfd ? execfd : get_errno(sys_openat(dirfd, exec_path, flags, mode)); + return execfd ? execfd : sys_openat(dirfd, exec_path, flags, mode); } for (fake_open = fakes; fake_open->filename; fake_open++) { @@ -5819,7 +5823,9 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, unlink(filename); if ((r = fake_open->fill(cpu_env, fd))) { + int e = errno; close(fd); + errno = e; return r; } lseek(fd, 0, SEEK_SET); @@ -5827,7 +5833,7 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, return fd; } - return get_errno(sys_openat(dirfd, path(pathname), flags, mode)); + return sys_openat(dirfd, path(pathname), flags, mode); } #define TIMER_MAGIC 0x0caf0000 -- cgit v1.1 From da7c8647e51f1f82fb98c40933d0950bca988a6c Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:27 +0100 Subject: linux-user: Reindent signal handling Some of the signal handling was a mess with a mixture of tabs and 8 space indents. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-3-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: just rebased] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/signal.c | 1543 ++++++++++++++++++++++++++------------------------- 1 file changed, 791 insertions(+), 752 deletions(-) (limited to 'linux-user') diff --git a/linux-user/signal.c b/linux-user/signal.c index 96e86c0..04c21d0 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -157,7 +157,7 @@ static void target_to_host_sigset_internal(sigset_t *d, 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) @@ -250,18 +250,18 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, tinfo->si_code = info->si_code; if (sig == TARGET_SIGILL || sig == TARGET_SIGFPE || sig == TARGET_SIGSEGV - || sig == TARGET_SIGBUS || sig == TARGET_SIGTRAP) { + || sig == TARGET_SIGBUS || sig == TARGET_SIGTRAP) { /* Should never come here, but who knows. The information for the target is irrelevant. */ tinfo->_sifields._sigfault._addr = 0; } else if (sig == TARGET_SIGIO) { tinfo->_sifields._sigpoll._band = info->si_band; - tinfo->_sifields._sigpoll._fd = info->si_fd; + tinfo->_sifields._sigpoll._fd = info->si_fd; } else if (sig == 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); + = host_to_target_waitstatus(info->si_status); tinfo->_sifields._sigchld._utime = info->si_utime; tinfo->_sifields._sigchld._stime = info->si_stime; } else if (sig >= TARGET_SIGRTMIN) { @@ -269,7 +269,7 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, 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; + = (abi_ulong)(unsigned long)info->si_value.sival_ptr; } } @@ -723,75 +723,75 @@ int do_sigaction(int sig, const struct target_sigaction *act, /* from the Linux kernel */ struct target_fpreg { - uint16_t significand[4]; - uint16_t exponent; + uint16_t significand[4]; + uint16_t exponent; }; struct target_fpxreg { - uint16_t significand[4]; - uint16_t exponent; - uint16_t padding[3]; + uint16_t significand[4]; + uint16_t exponent; + uint16_t padding[3]; }; struct target_xmmreg { - abi_ulong element[4]; + abi_ulong element[4]; }; struct target_fpstate { - /* Regular FPU environment */ - abi_ulong cw; - abi_ulong sw; - abi_ulong tag; - abi_ulong ipoff; - abi_ulong cssel; - abi_ulong dataoff; - abi_ulong datasel; - struct target_fpreg _st[8]; - uint16_t status; - uint16_t magic; /* 0xffff = regular FPU data only */ - - /* FXSR FPU environment */ - abi_ulong _fxsr_env[6]; /* FXSR FPU env is ignored */ - abi_ulong mxcsr; - abi_ulong reserved; - struct target_fpxreg _fxsr_st[8]; /* FXSR FPU reg data is ignored */ - struct target_xmmreg _xmm[8]; - abi_ulong padding[56]; + /* Regular FPU environment */ + abi_ulong cw; + abi_ulong sw; + abi_ulong tag; + abi_ulong ipoff; + abi_ulong cssel; + abi_ulong dataoff; + abi_ulong datasel; + struct target_fpreg _st[8]; + uint16_t status; + uint16_t magic; /* 0xffff = regular FPU data only */ + + /* FXSR FPU environment */ + abi_ulong _fxsr_env[6]; /* FXSR FPU env is ignored */ + abi_ulong mxcsr; + abi_ulong reserved; + struct target_fpxreg _fxsr_st[8]; /* FXSR FPU reg data is ignored */ + struct target_xmmreg _xmm[8]; + abi_ulong padding[56]; }; #define X86_FXSR_MAGIC 0x0000 struct target_sigcontext { - uint16_t gs, __gsh; - uint16_t fs, __fsh; - uint16_t es, __esh; - uint16_t ds, __dsh; - abi_ulong edi; - abi_ulong esi; - abi_ulong ebp; - abi_ulong esp; - abi_ulong ebx; - abi_ulong edx; - abi_ulong ecx; - abi_ulong eax; - abi_ulong trapno; - abi_ulong err; - abi_ulong eip; - uint16_t cs, __csh; - abi_ulong eflags; - abi_ulong esp_at_signal; - uint16_t ss, __ssh; - abi_ulong fpstate; /* pointer */ - abi_ulong oldmask; - abi_ulong cr2; + uint16_t gs, __gsh; + uint16_t fs, __fsh; + uint16_t es, __esh; + uint16_t ds, __dsh; + abi_ulong edi; + abi_ulong esi; + abi_ulong ebp; + abi_ulong esp; + abi_ulong ebx; + abi_ulong edx; + abi_ulong ecx; + abi_ulong eax; + abi_ulong trapno; + abi_ulong err; + abi_ulong eip; + uint16_t cs, __csh; + abi_ulong eflags; + abi_ulong esp_at_signal; + uint16_t ss, __ssh; + abi_ulong fpstate; /* pointer */ + abi_ulong oldmask; + abi_ulong cr2; }; 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 */ + 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 sigframe @@ -828,7 +828,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPUState *cs = CPU(x86_env_get_cpu(env)); uint16_t magic; - /* already locked in setup_frame() */ + /* 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); @@ -849,13 +849,13 @@ static void setup_sigcontext(struct target_sigcontext *sc, __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; + 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.. */ + /* non-iBCS2 extensions.. */ __put_user(mask, &sc->oldmask); __put_user(env->cr[2], &sc->cr2); } @@ -867,110 +867,112 @@ static void setup_sigcontext(struct target_sigcontext *sc, static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) { - unsigned long esp; + unsigned long esp; - /* Default to using normal stack */ - esp = env->regs[R_ESP]; - /* 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; + /* Default to using normal stack */ + esp = env->regs[R_ESP]; + /* 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 { - /* This is the legacy signal stack switching. */ - else + /* 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) { + !(ka->sa_flags & TARGET_SA_RESTORER) && + ka->sa_restorer) { esp = (unsigned long) ka->sa_restorer; - } - return (esp - frame_size) & -8ul; + } + } + return (esp - frame_size) & -8ul; } /* 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) + target_sigset_t *set, CPUX86State *env) { - abi_ulong frame_addr; - struct sigframe *frame; - int i; + abi_ulong frame_addr; + struct sigframe *frame; + int i; - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_frame(env, frame_addr); + 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; + 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)); + 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) { + /* 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); + } 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; + /* 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; + 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; + /* 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; + 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); + unlock_user_struct(frame, frame_addr, 1); - return; + return; give_sigsegv: - if (sig == TARGET_SIGSEGV) - ka->_sa_handler = TARGET_SIG_DFL; - force_sig(TARGET_SIGSEGV /* , current */); + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sig(TARGET_SIGSEGV /* , current */); } /* compare linux/arch/i386/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) + target_sigset_t *set, CPUX86State *env) { - abi_ulong frame_addr, addr; - struct rt_sigframe *frame; - int i; + abi_ulong frame_addr, addr; + struct rt_sigframe *frame; + int i; - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); + 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; + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto give_sigsegv; __put_user(sig, &frame->sig); - addr = frame_addr + offsetof(struct rt_sigframe, info); + addr = frame_addr + offsetof(struct rt_sigframe, info); __put_user(addr, &frame->pinfo); - addr = frame_addr + offsetof(struct rt_sigframe, uc); + addr = frame_addr + offsetof(struct rt_sigframe, uc); __put_user(addr, &frame->puc); tswap_siginfo(&frame->info, info); - /* Create the ucontext. */ + /* 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); @@ -985,81 +987,82 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, __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. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { + /* 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; - addr = frame_addr + offsetof(struct rt_sigframe, retcode); + } else { + uint16_t val16; + addr = frame_addr + offsetof(struct rt_sigframe, retcode); __put_user(addr, &frame->pretcode); - /* This is movl $,%eax ; int $0x80 */ + /* 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; + val16 = 0x80cd; __put_user(val16, (uint16_t *)(frame->retcode+5)); - } + } - /* Set up registers for signal handler */ - env->regs[R_ESP] = frame_addr; - env->eip = ka->_sa_handler; + /* 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; + 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); + unlock_user_struct(frame, frame_addr, 1); - return; + return; give_sigsegv: - if (sig == TARGET_SIGSEGV) - ka->_sa_handler = TARGET_SIG_DFL; - force_sig(TARGET_SIGSEGV /* , current */); + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sig(TARGET_SIGSEGV /* , current */); } static int restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc, int *peax) { - unsigned int err = 0; - abi_ulong fpstate_addr; - unsigned int tmpflags; - - 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->eip = tswapl(sc->eip); - - 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; - cpu_x86_frstor(env, fpstate_addr, 1); - } + unsigned int err = 0; + abi_ulong fpstate_addr; + unsigned int tmpflags; + + 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->eip = tswapl(sc->eip); + + 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; + cpu_x86_frstor(env, fpstate_addr, 1); + } - *peax = tswapl(sc->eax); - return err; + *peax = tswapl(sc->eax); + return err; badframe: - return 1; + return 1; } long do_sigreturn(CPUX86State *env) @@ -1096,32 +1099,34 @@ badframe: long do_rt_sigreturn(CPUX86State *env) { - abi_ulong frame_addr; - struct rt_sigframe *frame; - sigset_t set; - int eax; + abi_ulong frame_addr; + struct rt_sigframe *frame; + sigset_t set; + int eax; - frame_addr = env->regs[R_ESP] - 4; - 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); - do_sigprocmask(SIG_SETMASK, &set, NULL); + frame_addr = env->regs[R_ESP] - 4; + 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); + do_sigprocmask(SIG_SETMASK, &set, NULL); - if (restore_sigcontext(env, &frame->uc.tuc_mcontext, &eax)) - goto badframe; + if (restore_sigcontext(env, &frame->uc.tuc_mcontext, &eax)) { + goto badframe; + } - if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe, uc.tuc_stack), 0, - get_sp_from_cpustate(env)) == -EFAULT) - 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 eax; + unlock_user_struct(frame, frame_addr, 0); + return eax; badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return 0; + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; } #elif defined(TARGET_AARCH64) @@ -1402,27 +1407,27 @@ long do_sigreturn(CPUARMState *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; + 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 { @@ -1581,7 +1586,7 @@ get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) 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 *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) { abi_ulong handler = ka->_sa_handler; abi_ulong retcode; @@ -1691,42 +1696,44 @@ static void setup_sigframe_v2(struct target_ucontext_v2 *uc, 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; + 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)) - return; + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + return; + } - setup_sigcontext(&frame->sc, regs, set->sig[0]); + 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)); + setup_return(regs, ka, &frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct sigframe_v1, retcode)); - unlock_user_struct(frame, frame_addr, 1); + unlock_user_struct(frame, frame_addr, 1); } 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)); + 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)) - return; + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + return; + } - setup_sigframe_v2(&frame->uc, set, regs); + setup_sigframe_v2(&frame->uc, set, regs); - setup_return(regs, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe_v2, retcode)); + setup_return(regs, ka, &frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct sigframe_v2, retcode)); - unlock_user_struct(frame, frame_addr, 1); + unlock_user_struct(frame, frame_addr, 1); } static void setup_frame(int usig, struct target_sigaction *ka, @@ -1744,70 +1751,72 @@ 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; + 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)) - return /* 1 */; + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + return /* 1 */; + } - 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); + 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)); + /* 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)); + 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_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)); + 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; + env->regs[1] = info_addr; + env->regs[2] = uc_addr; - unlock_user_struct(frame, frame_addr, 1); + unlock_user_struct(frame, frame_addr, 1); } 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; + 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)) - return /* 1 */; + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + return /* 1 */; + } - 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); + 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_sigframe_v2(&frame->uc, set, env); - setup_return(env, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe_v2, retcode)); + 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; + env->regs[1] = info_addr; + env->regs[2] = uc_addr; - unlock_user_struct(frame, frame_addr, 1); + unlock_user_struct(frame, frame_addr, 1); } static void setup_rt_frame(int usig, struct target_sigaction *ka, @@ -1824,8 +1833,8 @@ static void setup_rt_frame(int usig, struct target_sigaction *ka, static int restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) { - int err = 0; - uint32_t cpsr; + int err = 0; + uint32_t cpsr; __get_user(env->regs[0], &sc->arm_r0); __get_user(env->regs[1], &sc->arm_r1); @@ -1848,55 +1857,57 @@ restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); #endif - err |= !valid_user_regs(env); + err |= !valid_user_regs(env); - return err; + 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; - } + abi_ulong frame_addr; + struct sigframe_v1 *frame = NULL; + target_sigset_t set; + sigset_t host_set; + int i; - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; + /* + * 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); - do_sigprocmask(SIG_SETMASK, &host_set, NULL); + target_to_host_sigset_internal(&host_set, &set); + do_sigprocmask(SIG_SETMASK, &host_set, NULL); - if (restore_sigcontext(env, &frame->sc)) - goto badframe; + 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); + /* 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 env->regs[0]; + unlock_user_struct(frame, frame_addr, 0); + return env->regs[0]; badframe: - force_sig(TARGET_SIGSEGV /* , current */); - return 0; + force_sig(TARGET_SIGSEGV /* , current */); + return 0; } static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) @@ -1987,7 +1998,7 @@ static int do_sigframe_return_v2(CPUARMState *env, target_ulong frame_addr, #if 0 /* Send SIGTRAP if we're single-stepping */ if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); + send_sig(SIGTRAP, current, 1); #endif return 0; @@ -1995,33 +2006,35 @@ static int do_sigframe_return_v2(CPUARMState *env, target_ulong frame_addr, 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; - } + 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 (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } - if (do_sigframe_return_v2(env, frame_addr, &frame->uc)) - goto badframe; + if (do_sigframe_return_v2(env, frame_addr, &frame->uc)) { + goto badframe; + } - unlock_user_struct(frame, frame_addr, 0); - return env->regs[0]; + unlock_user_struct(frame, frame_addr, 0); + return env->regs[0]; badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV /* , current */); - return 0; + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV /* , current */); + return 0; } long do_sigreturn(CPUARMState *env) @@ -2035,76 +2048,80 @@ long do_sigreturn(CPUARMState *env) static long do_rt_sigreturn_v1(CPUARMState *env) { - abi_ulong frame_addr; - struct rt_sigframe_v1 *frame = NULL; - sigset_t host_set; - - /* - * 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_rt_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } + abi_ulong frame_addr; + struct rt_sigframe_v1 *frame = NULL; + sigset_t host_set; - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; + /* + * 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_rt_sigreturn(env, frame_addr); + if (frame_addr & 7) { + goto badframe; + } - target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); - do_sigprocmask(SIG_SETMASK, &host_set, NULL); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); + do_sigprocmask(SIG_SETMASK, &host_set, NULL); - if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) - goto badframe; + if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { + goto badframe; + } - if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe_v1, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) - goto badframe; + if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe_v1, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) + goto badframe; #if 0 - /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); + /* 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 env->regs[0]; + unlock_user_struct(frame, frame_addr, 0); + return env->regs[0]; badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV /* , current */); - return 0; + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV /* , current */); + return 0; } static long do_rt_sigreturn_v2(CPUARMState *env) { - abi_ulong frame_addr; - struct rt_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_rt_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } + abi_ulong frame_addr; + struct rt_sigframe_v2 *frame = NULL; - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; + /* + * 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_rt_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, frame_addr, &frame->uc)) - goto badframe; + if (do_sigframe_return_v2(env, frame_addr, &frame->uc)) { + goto badframe; + } - unlock_user_struct(frame, frame_addr, 0); - return env->regs[0]; + unlock_user_struct(frame, frame_addr, 0); + return env->regs[0]; badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV /* , current */); - return 0; + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV /* , current */); + return 0; } long do_rt_sigreturn(CPUARMState *env) @@ -2122,83 +2139,83 @@ long do_rt_sigreturn(CPUARMState *env) /* This is what SunOS does, so shall I. */ struct target_sigcontext { - abi_ulong sigc_onstack; /* state to restore */ + abi_ulong sigc_onstack; /* state to restore */ - abi_ulong sigc_mask; /* sigmask to restore */ - abi_ulong sigc_sp; /* stack pointer */ - abi_ulong sigc_pc; /* program counter */ - abi_ulong sigc_npc; /* next program counter */ - abi_ulong sigc_psr; /* for condition codes etc */ - abi_ulong sigc_g1; /* User uses these two registers */ - abi_ulong sigc_o0; /* within the trampoline code. */ + abi_ulong sigc_mask; /* sigmask to restore */ + abi_ulong sigc_sp; /* stack pointer */ + abi_ulong sigc_pc; /* program counter */ + abi_ulong sigc_npc; /* next program counter */ + abi_ulong sigc_psr; /* for condition codes etc */ + abi_ulong sigc_g1; /* User uses these two registers */ + abi_ulong sigc_o0; /* within the trampoline code. */ - /* Now comes information regarding the users window set + /* Now comes information regarding the users window set * at the time of the signal. */ - abi_ulong sigc_oswins; /* outstanding windows */ + abi_ulong sigc_oswins; /* outstanding windows */ - /* stack ptrs for each regwin buf */ - char *sigc_spbuf[__SUNOS_MAXWIN]; + /* stack ptrs for each regwin buf */ + char *sigc_spbuf[__SUNOS_MAXWIN]; - /* Windows to restore after signal */ - struct { - abi_ulong locals[8]; - abi_ulong ins[8]; - } sigc_wbuf[__SUNOS_MAXWIN]; + /* Windows to restore after signal */ + struct { + abi_ulong locals[8]; + abi_ulong ins[8]; + } sigc_wbuf[__SUNOS_MAXWIN]; }; /* A Sparc stack frame */ struct sparc_stackf { - abi_ulong locals[8]; - abi_ulong ins[8]; - /* It's simpler to treat fp and callers_pc as elements of ins[] + abi_ulong locals[8]; + abi_ulong ins[8]; + /* It's simpler to treat fp and callers_pc as elements of ins[] * since we never need to access them ourselves. */ - char *structptr; - abi_ulong xargs[6]; - abi_ulong xxargs[1]; + char *structptr; + abi_ulong xargs[6]; + abi_ulong xxargs[1]; }; typedef struct { - struct { - abi_ulong psr; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong u_regs[16]; /* globals and ins */ - } si_regs; - int si_mask; + struct { + abi_ulong psr; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong u_regs[16]; /* globals and ins */ + } si_regs; + int si_mask; } __siginfo_t; typedef struct { - abi_ulong si_float_regs[32]; - unsigned long si_fsr; - unsigned long si_fpqdepth; - struct { - unsigned long *insn_addr; - unsigned long insn; - } si_fpqueue [16]; + abi_ulong si_float_regs[32]; + unsigned long si_fsr; + unsigned long si_fpqdepth; + struct { + unsigned long *insn_addr; + unsigned long insn; + } si_fpqueue [16]; } qemu_siginfo_fpu_t; struct target_signal_frame { - struct sparc_stackf ss; - __siginfo_t info; - abi_ulong fpu_save; - abi_ulong insns[2] __attribute__ ((aligned (8))); - abi_ulong extramask[TARGET_NSIG_WORDS - 1]; - abi_ulong extra_size; /* Should be 0 */ - qemu_siginfo_fpu_t fpu_state; + struct sparc_stackf ss; + __siginfo_t info; + abi_ulong fpu_save; + abi_ulong insns[2] __attribute__ ((aligned (8))); + abi_ulong extramask[TARGET_NSIG_WORDS - 1]; + abi_ulong extra_size; /* Should be 0 */ + qemu_siginfo_fpu_t fpu_state; }; struct target_rt_signal_frame { - struct sparc_stackf ss; - siginfo_t info; - abi_ulong regs[20]; - sigset_t mask; - abi_ulong fpu_save; - unsigned int insns[2]; - stack_t stack; - unsigned int extra_size; /* Should be 0 */ - qemu_siginfo_fpu_t fpu_state; + struct sparc_stackf ss; + siginfo_t info; + abi_ulong regs[20]; + sigset_t mask; + abi_ulong fpu_save; + unsigned int insns[2]; + stack_t stack; + unsigned int extra_size; /* Should be 0 */ + qemu_siginfo_fpu_t fpu_state; }; #define UREG_O0 16 @@ -2219,36 +2236,37 @@ static inline abi_ulong get_sigframe(struct target_sigaction *sa, CPUSPARCState *env, unsigned long framesize) { - abi_ulong sp; + abi_ulong sp; - sp = env->regwptr[UREG_FP]; + sp = env->regwptr[UREG_FP]; - /* This is the X/Open sanctioned signal stack switching. */ - if (sa->sa_flags & TARGET_SA_ONSTACK) { - if (!on_sig_stack(sp) - && !((target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size) & 7)) - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - return sp - framesize; + /* This is the X/Open sanctioned signal stack switching. */ + if (sa->sa_flags & TARGET_SA_ONSTACK) { + if (!on_sig_stack(sp) + && !((target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size) & 7)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + } + return sp - framesize; } static int setup___siginfo(__siginfo_t *si, CPUSPARCState *env, abi_ulong mask) { - int err = 0, i; + int err = 0, i; __put_user(env->psr, &si->si_regs.psr); __put_user(env->pc, &si->si_regs.pc); __put_user(env->npc, &si->si_regs.npc); __put_user(env->y, &si->si_regs.y); - for (i=0; i < 8; i++) { + for (i=0; i < 8; i++) { __put_user(env->gregs[i], &si->si_regs.u_regs[i]); - } - for (i=0; i < 8; i++) { + } + for (i=0; i < 8; i++) { __put_user(env->regwptr[UREG_I0 + i], &si->si_regs.u_regs[i+8]); - } + } __put_user(mask, &si->si_mask); - return err; + return err; } #if 0 @@ -2256,7 +2274,7 @@ static int setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ CPUSPARCState *env, unsigned long mask) { - int err = 0; + int err = 0; __put_user(mask, &sc->sigc_mask); __put_user(env->regwptr[UREG_SP], &sc->sigc_sp); @@ -2266,7 +2284,7 @@ setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ __put_user(env->gregs[1], &sc->sigc_g1); __put_user(env->regwptr[UREG_O0], &sc->sigc_o0); - return err; + return err; } #endif #define NF_ALIGNEDSZ (((sizeof(struct target_signal_frame) + 7) & (~7))) @@ -2274,90 +2292,90 @@ setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ static void setup_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUSPARCState *env) { - abi_ulong sf_addr; - struct target_signal_frame *sf; - int sigframe_size, err, i; + abi_ulong sf_addr; + struct target_signal_frame *sf; + int sigframe_size, err, i; - /* 1. Make sure everything is clean */ - //synchronize_user_stack(); + /* 1. Make sure everything is clean */ + //synchronize_user_stack(); - sigframe_size = NF_ALIGNEDSZ; - sf_addr = get_sigframe(ka, env, sigframe_size); - trace_user_setup_frame(env, sf_addr); + sigframe_size = NF_ALIGNEDSZ; + sf_addr = get_sigframe(ka, env, sigframe_size); + trace_user_setup_frame(env, sf_addr); - sf = lock_user(VERIFY_WRITE, sf_addr, - sizeof(struct target_signal_frame), 0); - if (!sf) - goto sigsegv; - + sf = lock_user(VERIFY_WRITE, sf_addr, + sizeof(struct target_signal_frame), 0); + if (!sf) { + goto sigsegv; + } #if 0 - if (invalid_frame_pointer(sf, sigframe_size)) - goto sigill_and_return; + if (invalid_frame_pointer(sf, sigframe_size)) + goto sigill_and_return; #endif - /* 2. Save the current process state */ - err = setup___siginfo(&sf->info, env, set->sig[0]); + /* 2. Save the current process state */ + err = setup___siginfo(&sf->info, env, set->sig[0]); __put_user(0, &sf->extra_size); - //save_fpu_state(regs, &sf->fpu_state); - //__put_user(&sf->fpu_state, &sf->fpu_save); + //save_fpu_state(regs, &sf->fpu_state); + //__put_user(&sf->fpu_state, &sf->fpu_save); __put_user(set->sig[0], &sf->info.si_mask); - for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { + for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { __put_user(set->sig[i + 1], &sf->extramask[i]); - } + } - for (i = 0; i < 8; i++) { + for (i = 0; i < 8; i++) { __put_user(env->regwptr[i + UREG_L0], &sf->ss.locals[i]); - } - for (i = 0; i < 8; i++) { + } + for (i = 0; i < 8; i++) { __put_user(env->regwptr[i + UREG_I0], &sf->ss.ins[i]); - } - if (err) - goto sigsegv; - - /* 3. signal handler back-trampoline and parameters */ - env->regwptr[UREG_FP] = sf_addr; - env->regwptr[UREG_I0] = sig; - env->regwptr[UREG_I1] = sf_addr + - offsetof(struct target_signal_frame, info); - env->regwptr[UREG_I2] = sf_addr + - offsetof(struct target_signal_frame, info); - - /* 4. signal handler */ - env->pc = ka->_sa_handler; - env->npc = (env->pc + 4); - /* 5. return to kernel instructions */ - if (ka->sa_restorer) - env->regwptr[UREG_I7] = ka->sa_restorer; - else { - uint32_t val32; - - env->regwptr[UREG_I7] = sf_addr + - offsetof(struct target_signal_frame, insns) - 2 * 4; - - /* mov __NR_sigreturn, %g1 */ - val32 = 0x821020d8; + } + if (err) + goto sigsegv; + + /* 3. signal handler back-trampoline and parameters */ + env->regwptr[UREG_FP] = sf_addr; + env->regwptr[UREG_I0] = sig; + env->regwptr[UREG_I1] = sf_addr + + offsetof(struct target_signal_frame, info); + env->regwptr[UREG_I2] = sf_addr + + offsetof(struct target_signal_frame, info); + + /* 4. signal handler */ + env->pc = ka->_sa_handler; + env->npc = (env->pc + 4); + /* 5. return to kernel instructions */ + if (ka->sa_restorer) { + env->regwptr[UREG_I7] = ka->sa_restorer; + } else { + uint32_t val32; + + env->regwptr[UREG_I7] = sf_addr + + offsetof(struct target_signal_frame, insns) - 2 * 4; + + /* mov __NR_sigreturn, %g1 */ + val32 = 0x821020d8; __put_user(val32, &sf->insns[0]); - /* t 0x10 */ - val32 = 0x91d02010; + /* t 0x10 */ + val32 = 0x91d02010; __put_user(val32, &sf->insns[1]); - if (err) - goto sigsegv; + if (err) + goto sigsegv; - /* Flush instruction space. */ - //flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); - // tb_flush(CPU(sparc_env_get_cpu(env))); - } - unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); - return; + /* Flush instruction space. */ + // flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); + // tb_flush(env); + } + unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); + return; #if 0 sigill_and_return: - force_sig(TARGET_SIGILL); + force_sig(TARGET_SIGILL); #endif sigsegv: - unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); - force_sig(TARGET_SIGSEGV); + unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); + force_sig(TARGET_SIGSEGV); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -2369,71 +2387,74 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, long do_sigreturn(CPUSPARCState *env) { - abi_ulong sf_addr; - struct target_signal_frame *sf; - uint32_t up_psr, pc, npc; - target_sigset_t set; - sigset_t host_set; - int err=0, i; + abi_ulong sf_addr; + struct target_signal_frame *sf; + uint32_t up_psr, pc, npc; + target_sigset_t set; + sigset_t host_set; + int err=0, i; - sf_addr = env->regwptr[UREG_FP]; - trace_user_do_sigreturn(env, sf_addr); - if (!lock_user_struct(VERIFY_READ, sf, sf_addr, 1)) - goto segv_and_exit; + sf_addr = env->regwptr[UREG_FP]; + trace_user_do_sigreturn(env, sf_addr); + if (!lock_user_struct(VERIFY_READ, sf, sf_addr, 1)) { + goto segv_and_exit; + } - /* 1. Make sure we are not getting garbage from the user */ + /* 1. Make sure we are not getting garbage from the user */ - if (sf_addr & 3) - goto segv_and_exit; + if (sf_addr & 3) + goto segv_and_exit; - __get_user(pc, &sf->info.si_regs.pc); - __get_user(npc, &sf->info.si_regs.npc); + __get_user(pc, &sf->info.si_regs.pc); + __get_user(npc, &sf->info.si_regs.npc); - if ((pc | npc) & 3) - goto segv_and_exit; + if ((pc | npc) & 3) { + goto segv_and_exit; + } - /* 2. Restore the state */ - __get_user(up_psr, &sf->info.si_regs.psr); + /* 2. Restore the state */ + __get_user(up_psr, &sf->info.si_regs.psr); - /* User can only change condition codes and FPU enabling in %psr. */ - env->psr = (up_psr & (PSR_ICC /* | PSR_EF */)) - | (env->psr & ~(PSR_ICC /* | PSR_EF */)); + /* User can only change condition codes and FPU enabling in %psr. */ + env->psr = (up_psr & (PSR_ICC /* | PSR_EF */)) + | (env->psr & ~(PSR_ICC /* | PSR_EF */)); - env->pc = pc; - env->npc = npc; - __get_user(env->y, &sf->info.si_regs.y); - for (i=0; i < 8; i++) { - __get_user(env->gregs[i], &sf->info.si_regs.u_regs[i]); - } - for (i=0; i < 8; i++) { - __get_user(env->regwptr[i + UREG_I0], &sf->info.si_regs.u_regs[i+8]); - } + env->pc = pc; + env->npc = npc; + __get_user(env->y, &sf->info.si_regs.y); + for (i=0; i < 8; i++) { + __get_user(env->gregs[i], &sf->info.si_regs.u_regs[i]); + } + for (i=0; i < 8; i++) { + __get_user(env->regwptr[i + UREG_I0], &sf->info.si_regs.u_regs[i+8]); + } - /* FIXME: implement FPU save/restore: + /* FIXME: implement FPU save/restore: * __get_user(fpu_save, &sf->fpu_save); * if (fpu_save) * err |= restore_fpu_state(env, fpu_save); */ - /* This is pretty much atomic, no amount locking would prevent + /* This is pretty much atomic, no amount locking would prevent * the races which exist anyways. */ - __get_user(set.sig[0], &sf->info.si_mask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(set.sig[i], &sf->extramask[i - 1]); - } + __get_user(set.sig[0], &sf->info.si_mask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(set.sig[i], &sf->extramask[i - 1]); + } - target_to_host_sigset_internal(&host_set, &set); - do_sigprocmask(SIG_SETMASK, &host_set, NULL); + target_to_host_sigset_internal(&host_set, &set); + do_sigprocmask(SIG_SETMASK, &host_set, NULL); - if (err) - goto segv_and_exit; - unlock_user_struct(sf, sf_addr, 0); - return env->regwptr[0]; + if (err) { + goto segv_and_exit; + } + unlock_user_struct(sf, sf_addr, 0); + return env->regwptr[0]; segv_and_exit: - unlock_user_struct(sf, sf_addr, 0); - force_sig(TARGET_SIGSEGV); + unlock_user_struct(sf, sf_addr, 0); + force_sig(TARGET_SIGSEGV); } long do_rt_sigreturn(CPUSPARCState *env) @@ -2522,13 +2543,15 @@ void sparc64_set_context(CPUSPARCState *env) unsigned int i; ucp_addr = env->regwptr[UREG_I0]; - if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) + if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) { goto do_sigsegv; + } grp = &ucp->tuc_mcontext.mc_gregs; __get_user(pc, &((*grp)[MC_PC])); __get_user(npc, &((*grp)[MC_NPC])); - if ((pc | npc) & 3) + if ((pc | npc) & 3) { goto do_sigsegv; + } if (env->regwptr[UREG_I1]) { target_sigset_t target_set; sigset_t set; @@ -2573,12 +2596,14 @@ void sparc64_set_context(CPUSPARCState *env) __get_user(i7, &(ucp->tuc_mcontext.mc_i7)); w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; - if (put_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), - abi_ulong) != 0) + if (put_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), + abi_ulong) != 0) { goto do_sigsegv; - if (put_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), - abi_ulong) != 0) + } + if (put_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), + abi_ulong) != 0) { goto do_sigsegv; + } /* FIXME this does not match how the kernel handles the FPU in * its sparc64_set_context implementation. In particular the FPU * is only restored if fenab is non-zero in: @@ -2601,7 +2626,7 @@ void sparc64_set_context(CPUSPARCState *env) &(ucp->tuc_mcontext.mc_fpregs.mcfpu_gsr)); unlock_user_struct(ucp, ucp_addr, 0); return; - do_sigsegv: +do_sigsegv: unlock_user_struct(ucp, ucp_addr, 0); force_sig(TARGET_SIGSEGV); } @@ -2619,8 +2644,9 @@ void sparc64_get_context(CPUSPARCState *env) sigset_t set; ucp_addr = env->regwptr[UREG_I0]; - if (!lock_user_struct(VERIFY_WRITE, ucp, ucp_addr, 0)) + if (!lock_user_struct(VERIFY_WRITE, ucp, ucp_addr, 0)) { goto do_sigsegv; + } mcp = &ucp->tuc_mcontext; grp = &mcp->mc_gregs; @@ -2670,12 +2696,14 @@ void sparc64_get_context(CPUSPARCState *env) w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; fp = i7 = 0; - if (get_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), - abi_ulong) != 0) + if (get_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), + abi_ulong) != 0) { goto do_sigsegv; - if (get_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), - abi_ulong) != 0) + } + if (get_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), + abi_ulong) != 0) { goto do_sigsegv; + } __put_user(fp, &(mcp->mc_fp)); __put_user(i7, &(mcp->mc_i7)); @@ -2697,7 +2725,7 @@ void sparc64_get_context(CPUSPARCState *env) goto do_sigsegv; unlock_user_struct(ucp, ucp_addr, 1); return; - do_sigsegv: +do_sigsegv: unlock_user_struct(ucp, ucp_addr, 1); force_sig(TARGET_SIGSEGV); } @@ -2787,7 +2815,7 @@ static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) } static inline void setup_sigcontext(CPUMIPSState *regs, - struct target_sigcontext *sc) + struct target_sigcontext *sc) { int i; @@ -2899,8 +2927,9 @@ static void setup_frame(int sig, struct target_sigaction * ka, 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 give_sigsegv; + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } install_sigtramp(frame->sf_code, TARGET_NR_sigreturn); @@ -2948,7 +2977,7 @@ long do_sigreturn(CPUMIPSState *regs) frame_addr = regs->active_tc.gpr[29]; trace_user_do_sigreturn(regs, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; + goto badframe; for(i = 0; i < TARGET_NSIG_WORDS; i++) { __get_user(target_set.sig[i], &frame->sf_mask.sig[i]); @@ -2994,8 +3023,9 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, 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; + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn); @@ -3053,8 +3083,9 @@ long do_rt_sigreturn(CPUMIPSState *env) frame_addr = env->active_tc.gpr[29]; trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); do_sigprocmask(SIG_SETMASK, &blocked, NULL); @@ -3062,8 +3093,8 @@ long do_rt_sigreturn(CPUMIPSState *env) restore_sigcontext(env, &frame->rs_uc.tuc_mcontext); if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, rs_uc.tuc_stack), - 0, get_sp_from_cpustate(env)) == -EFAULT) + offsetof(struct target_rt_sigframe, rs_uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) goto badframe; env->active_tc.PC = env->CP0_EPC; @@ -3134,7 +3165,7 @@ struct target_rt_sigframe #define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) SH3/4 */ static abi_ulong get_sigframe(struct target_sigaction *ka, - unsigned long sp, size_t frame_size) + unsigned long sp, size_t frame_size) { if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) { sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; @@ -3144,7 +3175,7 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, } static void setup_sigcontext(struct target_sigcontext *sc, - CPUSH4State *regs, unsigned long mask) + CPUSH4State *regs, unsigned long mask) { int i; @@ -3173,7 +3204,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, } static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc, - target_ulong *r0_p) + target_ulong *r0_p) { int i; @@ -3210,8 +3241,9 @@ static void setup_frame(int sig, struct target_sigaction *ka, frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto give_sigsegv; + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } setup_sigcontext(&frame->sc, regs, set->sig[0]); @@ -3258,8 +3290,9 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); trace_user_setup_rt_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto give_sigsegv; + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } tswap_siginfo(&frame->info, info); @@ -3273,7 +3306,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); setup_sigcontext(&frame->uc.tuc_mcontext, - regs, set->sig[0]); + regs, set->sig[0]); for(i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } @@ -3319,8 +3352,9 @@ long do_sigreturn(CPUSH4State *regs) frame_addr = regs->gregs[15]; trace_user_do_sigreturn(regs, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } __get_user(target_set.sig[0], &frame->sc.oldmask); for(i = 1; i < TARGET_NSIG_WORDS; i++) { @@ -3353,8 +3387,9 @@ long do_rt_sigreturn(CPUSH4State *regs) frame_addr = regs->gregs[15]; trace_user_do_rt_sigreturn(regs, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask); do_sigprocmask(SIG_SETMASK, &blocked, NULL); @@ -3362,9 +3397,10 @@ long do_rt_sigreturn(CPUSH4State *regs) restore_sigcontext(regs, &frame->uc.tuc_mcontext, &r0); if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, get_sp_from_cpustate(regs)) == -EFAULT) + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(regs)) == -EFAULT) { goto badframe; + } unlock_user_struct(frame, frame_addr, 0); return r0; @@ -3548,7 +3584,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, unlock_user_struct(frame, frame_addr, 1); return; - badframe: +badframe: force_sig(TARGET_SIGSEGV); } @@ -3576,7 +3612,7 @@ long do_sigreturn(CPUMBState *env) /* Restore blocked signals */ __get_user(target_set.sig[0], &frame->uc.tuc_mcontext.oldmask); for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); + __get_user(target_set.sig[i], &frame->extramask[i - 1]); } target_to_host_sigset_internal(&set, &target_set); do_sigprocmask(SIG_SETMASK, &set, NULL); @@ -3585,10 +3621,10 @@ long do_sigreturn(CPUMBState *env) /* We got here through a sigreturn syscall, our path back is via an rtb insn so setup r14 for that. */ env->regs[14] = env->sregs[SR_PC]; - + unlock_user_struct(frame, frame_addr, 0); return env->regs[10]; - badframe: +badframe: force_sig(TARGET_SIGSEGV); } @@ -3602,124 +3638,124 @@ long do_rt_sigreturn(CPUMBState *env) #elif defined(TARGET_CRIS) struct target_sigcontext { - struct target_pt_regs regs; /* needs to be first */ - uint32_t oldmask; - uint32_t usp; /* usp before stacking this gunk on it */ + struct target_pt_regs regs; /* needs to be first */ + uint32_t oldmask; + uint32_t usp; /* usp before stacking this gunk on it */ }; /* Signal frames. */ struct target_signal_frame { - struct target_sigcontext sc; - uint32_t extramask[TARGET_NSIG_WORDS - 1]; - uint16_t retcode[4]; /* Trampoline code. */ + struct target_sigcontext sc; + uint32_t extramask[TARGET_NSIG_WORDS - 1]; + uint16_t retcode[4]; /* Trampoline code. */ }; struct rt_signal_frame { - siginfo_t *pinfo; - void *puc; - siginfo_t info; - struct ucontext uc; - uint16_t retcode[4]; /* Trampoline code. */ + siginfo_t *pinfo; + void *puc; + siginfo_t info; + struct ucontext uc; + uint16_t retcode[4]; /* Trampoline code. */ }; static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) { - __put_user(env->regs[0], &sc->regs.r0); - __put_user(env->regs[1], &sc->regs.r1); - __put_user(env->regs[2], &sc->regs.r2); - __put_user(env->regs[3], &sc->regs.r3); - __put_user(env->regs[4], &sc->regs.r4); - __put_user(env->regs[5], &sc->regs.r5); - __put_user(env->regs[6], &sc->regs.r6); - __put_user(env->regs[7], &sc->regs.r7); - __put_user(env->regs[8], &sc->regs.r8); - __put_user(env->regs[9], &sc->regs.r9); - __put_user(env->regs[10], &sc->regs.r10); - __put_user(env->regs[11], &sc->regs.r11); - __put_user(env->regs[12], &sc->regs.r12); - __put_user(env->regs[13], &sc->regs.r13); - __put_user(env->regs[14], &sc->usp); - __put_user(env->regs[15], &sc->regs.acr); - __put_user(env->pregs[PR_MOF], &sc->regs.mof); - __put_user(env->pregs[PR_SRP], &sc->regs.srp); - __put_user(env->pc, &sc->regs.erp); + __put_user(env->regs[0], &sc->regs.r0); + __put_user(env->regs[1], &sc->regs.r1); + __put_user(env->regs[2], &sc->regs.r2); + __put_user(env->regs[3], &sc->regs.r3); + __put_user(env->regs[4], &sc->regs.r4); + __put_user(env->regs[5], &sc->regs.r5); + __put_user(env->regs[6], &sc->regs.r6); + __put_user(env->regs[7], &sc->regs.r7); + __put_user(env->regs[8], &sc->regs.r8); + __put_user(env->regs[9], &sc->regs.r9); + __put_user(env->regs[10], &sc->regs.r10); + __put_user(env->regs[11], &sc->regs.r11); + __put_user(env->regs[12], &sc->regs.r12); + __put_user(env->regs[13], &sc->regs.r13); + __put_user(env->regs[14], &sc->usp); + __put_user(env->regs[15], &sc->regs.acr); + __put_user(env->pregs[PR_MOF], &sc->regs.mof); + __put_user(env->pregs[PR_SRP], &sc->regs.srp); + __put_user(env->pc, &sc->regs.erp); } static void restore_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) { - __get_user(env->regs[0], &sc->regs.r0); - __get_user(env->regs[1], &sc->regs.r1); - __get_user(env->regs[2], &sc->regs.r2); - __get_user(env->regs[3], &sc->regs.r3); - __get_user(env->regs[4], &sc->regs.r4); - __get_user(env->regs[5], &sc->regs.r5); - __get_user(env->regs[6], &sc->regs.r6); - __get_user(env->regs[7], &sc->regs.r7); - __get_user(env->regs[8], &sc->regs.r8); - __get_user(env->regs[9], &sc->regs.r9); - __get_user(env->regs[10], &sc->regs.r10); - __get_user(env->regs[11], &sc->regs.r11); - __get_user(env->regs[12], &sc->regs.r12); - __get_user(env->regs[13], &sc->regs.r13); - __get_user(env->regs[14], &sc->usp); - __get_user(env->regs[15], &sc->regs.acr); - __get_user(env->pregs[PR_MOF], &sc->regs.mof); - __get_user(env->pregs[PR_SRP], &sc->regs.srp); - __get_user(env->pc, &sc->regs.erp); + __get_user(env->regs[0], &sc->regs.r0); + __get_user(env->regs[1], &sc->regs.r1); + __get_user(env->regs[2], &sc->regs.r2); + __get_user(env->regs[3], &sc->regs.r3); + __get_user(env->regs[4], &sc->regs.r4); + __get_user(env->regs[5], &sc->regs.r5); + __get_user(env->regs[6], &sc->regs.r6); + __get_user(env->regs[7], &sc->regs.r7); + __get_user(env->regs[8], &sc->regs.r8); + __get_user(env->regs[9], &sc->regs.r9); + __get_user(env->regs[10], &sc->regs.r10); + __get_user(env->regs[11], &sc->regs.r11); + __get_user(env->regs[12], &sc->regs.r12); + __get_user(env->regs[13], &sc->regs.r13); + __get_user(env->regs[14], &sc->usp); + __get_user(env->regs[15], &sc->regs.acr); + __get_user(env->pregs[PR_MOF], &sc->regs.mof); + __get_user(env->pregs[PR_SRP], &sc->regs.srp); + __get_user(env->pc, &sc->regs.erp); } static abi_ulong get_sigframe(CPUCRISState *env, int framesize) { - abi_ulong sp; - /* Align the stack downwards to 4. */ - sp = (env->regs[R_SP] & ~3); - return sp - framesize; + abi_ulong sp; + /* Align the stack downwards to 4. */ + sp = (env->regs[R_SP] & ~3); + return sp - framesize; } static void setup_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUCRISState *env) { - struct target_signal_frame *frame; - abi_ulong frame_addr; - int i; - - frame_addr = get_sigframe(env, sizeof *frame); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto badframe; - - /* - * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't - * use this trampoline anymore but it sets it up for GDB. - * In QEMU, using the trampoline simplifies things a bit so we use it. - * - * This is movu.w __NR_sigreturn, r9; break 13; - */ + struct target_signal_frame *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(env, sizeof *frame); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto badframe; + + /* + * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't + * use this trampoline anymore but it sets it up for GDB. + * In QEMU, using the trampoline simplifies things a bit so we use it. + * + * This is movu.w __NR_sigreturn, r9; break 13; + */ __put_user(0x9c5f, frame->retcode+0); __put_user(TARGET_NR_sigreturn, frame->retcode + 1); __put_user(0xe93d, frame->retcode + 2); - /* Save the mask. */ + /* Save the mask. */ __put_user(set->sig[0], &frame->sc.oldmask); for(i = 1; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->extramask[i - 1]); } - setup_sigcontext(&frame->sc, env); + setup_sigcontext(&frame->sc, env); - /* Move the stack and setup the arguments for the handler. */ - env->regs[R_SP] = frame_addr; - env->regs[10] = sig; - env->pc = (unsigned long) ka->_sa_handler; - /* Link SRP so the guest returns through the trampoline. */ - env->pregs[PR_SRP] = frame_addr + offsetof(typeof(*frame), retcode); + /* Move the stack and setup the arguments for the handler. */ + env->regs[R_SP] = frame_addr; + env->regs[10] = sig; + env->pc = (unsigned long) ka->_sa_handler; + /* Link SRP so the guest returns through the trampoline. */ + env->pregs[PR_SRP] = frame_addr + offsetof(typeof(*frame), retcode); - unlock_user_struct(frame, frame_addr, 1); - return; - badframe: - force_sig(TARGET_SIGSEGV); + unlock_user_struct(frame, frame_addr, 1); + return; +badframe: + force_sig(TARGET_SIGSEGV); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -3731,31 +3767,32 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, long do_sigreturn(CPUCRISState *env) { - struct target_signal_frame *frame; - abi_ulong frame_addr; - target_sigset_t target_set; - sigset_t set; - int i; + struct target_signal_frame *frame; + abi_ulong frame_addr; + target_sigset_t target_set; + sigset_t set; + int i; - frame_addr = env->regs[R_SP]; - trace_user_do_sigreturn(env, frame_addr); - /* Make sure the guest isn't playing games. */ - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) - goto badframe; + frame_addr = env->regs[R_SP]; + trace_user_do_sigreturn(env, frame_addr); + /* Make sure the guest isn't playing games. */ + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) { + goto badframe; + } - /* Restore blocked signals */ + /* Restore blocked signals */ __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { + 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); - do_sigprocmask(SIG_SETMASK, &set, NULL); + } + target_to_host_sigset_internal(&set, &target_set); + do_sigprocmask(SIG_SETMASK, &set, NULL); - restore_sigcontext(&frame->sc, env); - unlock_user_struct(frame, frame_addr, 0); - return env->regs[10]; - badframe: - force_sig(TARGET_SIGSEGV); + restore_sigcontext(&frame->sc, env); + unlock_user_struct(frame, frame_addr, 0); + return env->regs[10]; +badframe: + force_sig(TARGET_SIGSEGV); } long do_rt_sigreturn(CPUCRISState *env) @@ -3841,8 +3878,8 @@ badframe: /* Set up a signal frame. */ static void setup_sigcontext(struct target_sigcontext *sc, - CPUOpenRISCState *regs, - unsigned long mask) + CPUOpenRISCState *regs, + unsigned long mask) { unsigned long usp = regs->gpr[1]; @@ -4100,7 +4137,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, 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; + goto give_sigsegv; } __put_user(set->sig[0], &frame->sc.oldmask[0]); @@ -4113,13 +4150,13 @@ static void setup_frame(int sig, struct target_sigaction *ka, /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa_flags & TARGET_SA_RESTORER) { - env->regs[14] = (unsigned long) - ka->sa_restorer | PSW_ADDR_AMODE; + env->regs[14] = (unsigned long) + ka->sa_restorer | PSW_ADDR_AMODE; } else { - env->regs[14] = (unsigned long) - frame->retcode | PSW_ADDR_AMODE; - __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, - (uint16_t *)(frame->retcode)); + env->regs[14] = (unsigned long) + frame->retcode | PSW_ADDR_AMODE; + __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, + (uint16_t *)(frame->retcode)); } /* Set up backchain. */ @@ -4167,12 +4204,12 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user((abi_ulong)0, (abi_ulong *)&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); + &frame->uc.tuc_stack.ss_flags); __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); save_sigregs(env, &frame->uc.tuc_mcontext); for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user((abi_ulong)set->sig[i], - (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); + (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); } /* Set up to return from userspace. If provided, use a stub @@ -4423,15 +4460,15 @@ struct target_sigframe { #define TARGET_TRAMP_SIZE 6 struct target_rt_sigframe { - /* sys_rt_sigreturn requires the ucontext be the first field */ - struct target_ucontext uc; - target_ulong _unused[2]; - uint32_t trampoline[TARGET_TRAMP_SIZE]; - target_ulong pinfo; /* struct siginfo __user * */ - target_ulong puc; /* void __user * */ - struct target_siginfo info; - /* 64 bit ABI allows for 288 bytes below sp before decrementing it. */ - char abigap[288]; + /* sys_rt_sigreturn requires the ucontext be the first field */ + struct target_ucontext uc; + target_ulong _unused[2]; + uint32_t trampoline[TARGET_TRAMP_SIZE]; + target_ulong pinfo; /* struct siginfo __user * */ + target_ulong puc; /* void __user * */ + struct target_siginfo info; + /* 64 bit ABI allows for 288 bytes below sp before decrementing it. */ + char abigap[288]; } __attribute__((aligned(16))); #else @@ -4466,7 +4503,7 @@ static target_ulong get_sigframe(struct target_sigaction *ka, oldsp = env->gpr[1]; if ((ka->sa_flags & TARGET_SA_ONSTACK) && - (sas_ss_flags(oldsp) == 0)) { + (sas_ss_flags(oldsp) == 0)) { oldsp = (target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size); } @@ -4488,7 +4525,7 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) /* Save general registers. */ for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - __put_user(env->gpr[i], &frame->mc_gregs[i]); + __put_user(env->gpr[i], &frame->mc_gregs[i]); } __put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); __put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); @@ -4925,7 +4962,7 @@ struct target_sigframe abi_ulong extramask[TARGET_NSIG_WORDS-1]; struct target_sigcontext sc; }; - + typedef int target_greg_t; #define TARGET_NGREG 18 typedef target_greg_t target_gregset_t[TARGET_NGREG]; @@ -4964,7 +5001,7 @@ struct target_rt_sigframe }; static void setup_sigcontext(struct target_sigcontext *sc, CPUM68KState *env, - abi_ulong mask) + abi_ulong mask) { __put_user(mask, &sc->sc_mask); __put_user(env->aregs[7], &sc->sc_usp); @@ -5022,8 +5059,9 @@ static void setup_frame(int sig, struct target_sigaction *ka, 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; + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } __put_user(sig, &frame->sig); @@ -5044,7 +5082,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, /* moveq #,d0; trap #0 */ __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), - (uint32_t *)(frame->retcode)); + (uint32_t *)(frame->retcode)); /* Set up to return from userspace */ @@ -5085,7 +5123,7 @@ static inline int target_rt_setup_ucontext(struct target_ucontext *uc, return 0; } - + static inline int target_rt_restore_ucontext(CPUM68KState *env, struct target_ucontext *uc, int *pd0) @@ -5139,8 +5177,9 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, 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; + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } __put_user(sig, &frame->sig); @@ -5159,13 +5198,13 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); __put_user(sas_ss_flags(env->aregs[7]), - &frame->uc.tuc_stack.ss_flags); + &frame->uc.tuc_stack.ss_flags); __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); err |= target_rt_setup_ucontext(&frame->uc, env); if (err) - goto give_sigsegv; + goto give_sigsegv; for(i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); @@ -5316,7 +5355,7 @@ struct target_rt_sigframe { #define INSN_CALLSYS 0x00000083 static void setup_sigcontext(struct target_sigcontext *sc, CPUAlphaState *env, - abi_ulong frame_addr, target_sigset_t *set) + abi_ulong frame_addr, target_sigset_t *set) { int i; @@ -5342,7 +5381,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPUAlphaState *env, } static void restore_sigcontext(CPUAlphaState *env, - struct target_sigcontext *sc) + struct target_sigcontext *sc) { uint64_t fpcr; int i; @@ -5402,7 +5441,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, unlock_user_struct(frame, frame_addr, 1); if (err) { - give_sigsegv: +give_sigsegv: if (sig == TARGET_SIGSEGV) { ka->_sa_handler = TARGET_SIG_DFL; } @@ -5459,8 +5498,8 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, } if (err) { - give_sigsegv: - if (sig == TARGET_SIGSEGV) { +give_sigsegv: + if (sig == TARGET_SIGSEGV) { ka->_sa_handler = TARGET_SIG_DFL; } force_sig(TARGET_SIGSEGV); @@ -5495,7 +5534,7 @@ long do_sigreturn(CPUAlphaState *env) unlock_user_struct(sc, sc_addr, 0); return env->ir[IR_V0]; - badframe: +badframe: force_sig(TARGET_SIGSEGV); } @@ -5523,7 +5562,7 @@ long do_rt_sigreturn(CPUAlphaState *env) return env->ir[IR_V0]; - badframe: +badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); } @@ -5690,14 +5729,14 @@ long do_rt_sigreturn(CPUTLGState *env) #else static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUArchState *env) + target_sigset_t *set, CPUArchState *env) { fprintf(stderr, "setup_frame: not implemented\n"); } static void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, - target_sigset_t *set, CPUArchState *env) + target_sigset_t *set, CPUArchState *env) { fprintf(stderr, "setup_rt_frame: not implemented\n"); } -- cgit v1.1 From 14896d32814c78ed5b97a22fbd9e5a6cddc2de80 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:28 +0100 Subject: linux-user: Define TARGET_ERESTART* errno values Define TARGET_ERESTARTSYS; like the kernel, we will use this to indicate that a guest system call should be restarted. We use the same value the kernel does for this, 512. Signed-off-by: Timothy Edward Baldwin [PMM: split out from the patch which moves and renumbers TARGET_QEMU_ESIGRETURN, add comment on usage] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/errno_defs.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'linux-user') diff --git a/linux-user/errno_defs.h b/linux-user/errno_defs.h index 8a1cf76..b7a8c9f 100644 --- a/linux-user/errno_defs.h +++ b/linux-user/errno_defs.h @@ -139,3 +139,11 @@ /* for robust mutexes */ #define TARGET_EOWNERDEAD 130 /* Owner died */ #define TARGET_ENOTRECOVERABLE 131 /* State not recoverable */ + +/* QEMU internal, not visible to the guest. This is returned when a + * system call should be restarted, to tell the main loop that it + * should wind the guest PC backwards so it will re-execute the syscall + * after handling any pending signals. They match with the ones the guest + * kernel uses for the same purpose. + */ +#define TARGET_ERESTARTSYS 512 /* Restart system call (if SA_RESTART) */ -- cgit v1.1 From 499b5d176a77773cab5479264a3d5067176df21c Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:29 +0100 Subject: linux-user: Renumber TARGET_QEMU_ESIGRETURN, make it not arch-specific Currently we define a QEMU-internal errno TARGET_QEMU_ESIGRETURN only on the MIPS and PPC targets; move this to errno_defs.h so it is available for all architectures, and renumber it to 513. We pick 513 because this is safe from future use as a system call return value: Linux uses it as ERESTART_NOINTR internally and never allows that errno to escape to userspace. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-4-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: TARGET_ERESTARTSYS split out into preceding patch, add comment] Reviewed-by: Peter Maydell Reviewed-by: Laurent Vivier Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/errno_defs.h | 9 +++++++++ linux-user/mips/target_syscall.h | 4 ---- linux-user/mips64/target_syscall.h | 4 ---- linux-user/ppc/target_syscall.h | 2 -- 4 files changed, 9 insertions(+), 10 deletions(-) (limited to 'linux-user') diff --git a/linux-user/errno_defs.h b/linux-user/errno_defs.h index b7a8c9f..65522c4 100644 --- a/linux-user/errno_defs.h +++ b/linux-user/errno_defs.h @@ -147,3 +147,12 @@ * kernel uses for the same purpose. */ #define TARGET_ERESTARTSYS 512 /* Restart system call (if SA_RESTART) */ + +/* QEMU internal, not visible to the guest. This is returned by the + * do_sigreturn() code after a successful sigreturn syscall, to indicate + * that it has correctly set the guest registers and so the main loop + * should not touch them. We use the value the guest would use for + * ERESTART_NOINTR (which is kernel internal) to guarantee that we won't + * clash with a valid guest errno now or in the future. + */ +#define TARGET_QEMU_ESIGRETURN 513 /* Return from signal */ diff --git a/linux-user/mips/target_syscall.h b/linux-user/mips/target_syscall.h index 68db160..e8e305c 100644 --- a/linux-user/mips/target_syscall.h +++ b/linux-user/mips/target_syscall.h @@ -222,10 +222,6 @@ struct target_pt_regs { #define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */ - -/* Nasty hack: define a fake errno value for use by sigreturn. */ -#define TARGET_QEMU_ESIGRETURN 255 - #define UNAME_MACHINE "mips" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/mips64/target_syscall.h b/linux-user/mips64/target_syscall.h index 0e0c2d2..5789e86 100644 --- a/linux-user/mips64/target_syscall.h +++ b/linux-user/mips64/target_syscall.h @@ -219,10 +219,6 @@ struct target_pt_regs { #define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */ - -/* Nasty hack: define a fake errno value for use by sigreturn. */ -#define TARGET_QEMU_ESIGRETURN 255 - #define UNAME_MACHINE "mips64" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/ppc/target_syscall.h b/linux-user/ppc/target_syscall.h index 35cab59..7ca83c2 100644 --- a/linux-user/ppc/target_syscall.h +++ b/linux-user/ppc/target_syscall.h @@ -53,8 +53,6 @@ struct target_revectored_struct { abi_ulong __map[8]; /* 256 bits */ }; -/* Nasty hack: define a fake errno value for use by sigreturn. */ -#define TARGET_QEMU_ESIGRETURN 255 /* * flags masks -- cgit v1.1 From 0284b03ba3f47da53b6b46293a3d586c08829f7e Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:30 +0100 Subject: linux-user: Support for restarting system calls for x86 targets Update the x86 main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * set all guest CPU state within signal.c code rather than passing it back out as the "return code" from do_sigreturn() * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch EAX Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-5-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: Commit message tweaks; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 47 +++++++++++++++++++++++++++++------------------ linux-user/signal.c | 15 +++++++-------- linux-user/syscall.c | 2 -- 3 files changed, 36 insertions(+), 28 deletions(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index 95ed11d..da5a033 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -285,6 +285,7 @@ void cpu_loop(CPUX86State *env) CPUState *cs = CPU(x86_env_get_cpu(env)); int trapnr; abi_ulong pc; + abi_ulong ret; target_siginfo_t info; for(;;) { @@ -294,28 +295,38 @@ void cpu_loop(CPUX86State *env) switch(trapnr) { case 0x80: /* linux syscall from int $0x80 */ - env->regs[R_EAX] = do_syscall(env, - env->regs[R_EAX], - env->regs[R_EBX], - env->regs[R_ECX], - env->regs[R_EDX], - env->regs[R_ESI], - env->regs[R_EDI], - env->regs[R_EBP], - 0, 0); + ret = do_syscall(env, + env->regs[R_EAX], + env->regs[R_EBX], + env->regs[R_ECX], + env->regs[R_EDX], + env->regs[R_ESI], + env->regs[R_EDI], + env->regs[R_EBP], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->eip -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[R_EAX] = ret; + } break; #ifndef TARGET_ABI32 case EXCP_SYSCALL: /* linux syscall from syscall instruction */ - env->regs[R_EAX] = do_syscall(env, - env->regs[R_EAX], - env->regs[R_EDI], - env->regs[R_ESI], - env->regs[R_EDX], - env->regs[10], - env->regs[8], - env->regs[9], - 0, 0); + ret = do_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[10], + env->regs[8], + env->regs[9], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->eip -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[R_EAX] = ret; + } break; #endif case EXCP0B_NOSEG: diff --git a/linux-user/signal.c b/linux-user/signal.c index 04c21d0..11ddd05 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1024,7 +1024,7 @@ give_sigsegv: } static int -restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc, int *peax) +restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) { unsigned int err = 0; abi_ulong fpstate_addr; @@ -1042,6 +1042,7 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc, int *peax) 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); cpu_x86_load_seg(env, R_CS, lduw_p(&sc->cs) | 3); @@ -1059,7 +1060,6 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc, int *peax) cpu_x86_frstor(env, fpstate_addr, 1); } - *peax = tswapl(sc->eax); return err; badframe: return 1; @@ -1071,7 +1071,7 @@ long do_sigreturn(CPUX86State *env) abi_ulong frame_addr = env->regs[R_ESP] - 8; target_sigset_t target_set; sigset_t set; - int eax, i; + int i; trace_user_do_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) @@ -1086,10 +1086,10 @@ long do_sigreturn(CPUX86State *env) do_sigprocmask(SIG_SETMASK, &set, NULL); /* restore registers */ - if (restore_sigcontext(env, &frame->sc, &eax)) + if (restore_sigcontext(env, &frame->sc)) goto badframe; unlock_user_struct(frame, frame_addr, 0); - return eax; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); @@ -1102,7 +1102,6 @@ long do_rt_sigreturn(CPUX86State *env) abi_ulong frame_addr; struct rt_sigframe *frame; sigset_t set; - int eax; frame_addr = env->regs[R_ESP] - 4; trace_user_do_rt_sigreturn(env, frame_addr); @@ -1111,7 +1110,7 @@ long do_rt_sigreturn(CPUX86State *env) target_to_host_sigset(&set, &frame->uc.tuc_sigmask); do_sigprocmask(SIG_SETMASK, &set, NULL); - if (restore_sigcontext(env, &frame->uc.tuc_mcontext, &eax)) { + if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { goto badframe; } @@ -1121,7 +1120,7 @@ long do_rt_sigreturn(CPUX86State *env) } unlock_user_struct(frame, frame_addr, 0); - return eax; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index f4c2e19..a4a1af7 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6940,12 +6940,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #ifdef TARGET_NR_sigreturn case TARGET_NR_sigreturn: - /* NOTE: ret is eax, so not transcoding must be done */ ret = do_sigreturn(cpu_env); break; #endif case TARGET_NR_rt_sigreturn: - /* NOTE: ret is eax, so not transcoding must be done */ ret = do_rt_sigreturn(cpu_env); break; case TARGET_NR_sethostname: -- cgit v1.1 From f0267ef7115656119bf00ed77857789adc036bda Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:31 +0100 Subject: linux-user: Support for restarting system calls for ARM targets Update the 32-bit and 64-bit ARM main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * set all guest CPU state within signal.c code on sigreturn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-6-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: tweak commit message; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/arm/target_signal.h | 1 + linux-user/main.c | 48 ++++++++++++++++++++++++++---------------- linux-user/signal.c | 10 ++++----- 3 files changed, 36 insertions(+), 23 deletions(-) (limited to 'linux-user') diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h index 2b32813..fb31f4c 100644 --- a/linux-user/arm/target_signal.h +++ b/linux-user/arm/target_signal.h @@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) return state->regs[13]; } + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/main.c b/linux-user/main.c index da5a033..7916efc 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -727,6 +727,7 @@ void cpu_loop(CPUARMState *env) unsigned int n, insn; target_siginfo_t info; uint32_t addr; + abi_ulong ret; for(;;) { cpu_exec_start(cs); @@ -865,15 +866,20 @@ void cpu_loop(CPUARMState *env) break; } } else { - env->regs[0] = do_syscall(env, - n, - env->regs[0], - env->regs[1], - env->regs[2], - env->regs[3], - env->regs[4], - env->regs[5], - 0, 0); + ret = do_syscall(env, + n, + env->regs[0], + env->regs[1], + env->regs[2], + env->regs[3], + env->regs[4], + env->regs[5], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->regs[15] -= env->thumb ? 2 : 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[0] = ret; + } } } else { goto error; @@ -1056,6 +1062,7 @@ void cpu_loop(CPUARMState *env) { CPUState *cs = CPU(arm_env_get_cpu(env)); int trapnr, sig; + abi_long ret; target_siginfo_t info; for (;;) { @@ -1065,15 +1072,20 @@ void cpu_loop(CPUARMState *env) switch (trapnr) { case EXCP_SWI: - env->xregs[0] = do_syscall(env, - env->xregs[8], - env->xregs[0], - env->xregs[1], - env->xregs[2], - env->xregs[3], - env->xregs[4], - env->xregs[5], - 0, 0); + ret = do_syscall(env, + env->xregs[8], + env->xregs[0], + env->xregs[1], + env->xregs[2], + env->xregs[3], + env->xregs[4], + env->xregs[5], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->xregs[0] = ret; + } break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 11ddd05..14e58b0 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -1390,7 +1390,7 @@ long do_rt_sigreturn(CPUARMState *env) } unlock_user_struct(frame, frame_addr, 0); - return env->xregs[0]; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); @@ -1902,7 +1902,7 @@ static long do_sigreturn_v1(CPUARMState *env) send_sig(SIGTRAP, current, 1); #endif unlock_user_struct(frame, frame_addr, 0); - return env->regs[0]; + return -TARGET_QEMU_ESIGRETURN; badframe: force_sig(TARGET_SIGSEGV /* , current */); @@ -2028,7 +2028,7 @@ static long do_sigreturn_v2(CPUARMState *env) } unlock_user_struct(frame, frame_addr, 0); - return env->regs[0]; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); @@ -2082,7 +2082,7 @@ static long do_rt_sigreturn_v1(CPUARMState *env) send_sig(SIGTRAP, current, 1); #endif unlock_user_struct(frame, frame_addr, 0); - return env->regs[0]; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); @@ -2115,7 +2115,7 @@ static long do_rt_sigreturn_v2(CPUARMState *env) } unlock_user_struct(frame, frame_addr, 0); - return env->regs[0]; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); -- cgit v1.1 From 2eb3ae27ec3b797eba16338c08dfac23465f0d7b Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:32 +0100 Subject: linux-user: Support for restarting system calls for MIPS targets Update the MIPS main loop code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn (We already handle TARGET_QEMU_ESIGRETURN.) Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-7-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: tweak commit message; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 4 ++++ linux-user/mips/target_signal.h | 1 + linux-user/mips64/target_signal.h | 1 + 3 files changed, 6 insertions(+) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index 7916efc..73e3668 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2528,6 +2528,10 @@ done_syscall: env->active_tc.gpr[8], env->active_tc.gpr[9], env->active_tc.gpr[10], env->active_tc.gpr[11]); # endif /* O32 */ + if (ret == -TARGET_ERESTARTSYS) { + env->active_tc.PC -= 4; + break; + } if (ret == -TARGET_QEMU_ESIGRETURN) { /* Returning from a successful sigreturn syscall. Avoid clobbering register state. */ diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h index 6e1dc8b..460cc9f 100644 --- a/linux-user/mips/target_signal.h +++ b/linux-user/mips/target_signal.h @@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) return state->active_tc.gpr[29]; } + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h index 5fb6a2c..a2dc514 100644 --- a/linux-user/mips64/target_signal.h +++ b/linux-user/mips64/target_signal.h @@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) return state->active_tc.gpr[29]; } + #endif /* TARGET_SIGNAL_H */ -- cgit v1.1 From 6db9d00e2f05db0dfcc1a4d8b811fdd40170c91c Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:33 +0100 Subject: linux-user: Support for restarting system calls for PPC targets Update the PPC main loop code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn (We already handle TARGET_QEMU_ESIGRETURN.) Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-8-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: tweak commit message; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 4 ++++ linux-user/ppc/target_signal.h | 1 + 2 files changed, 5 insertions(+) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index 73e3668..dfc098f 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1987,6 +1987,10 @@ void cpu_loop(CPUPPCState *env) ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], env->gpr[5], env->gpr[6], env->gpr[7], env->gpr[8], 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->nip -= 4; + break; + } if (ret == (target_ulong)(-TARGET_QEMU_ESIGRETURN)) { /* Returning from a successful sigreturn syscall. Avoid corrupting register state. */ diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h index a93b5cf..4f01dd4 100644 --- a/linux-user/ppc/target_signal.h +++ b/linux-user/ppc/target_signal.h @@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUPPCState *state) return state->gpr[1]; } + #endif /* TARGET_SIGNAL_H */ -- cgit v1.1 From c0bea68f9ea48f0dea7a06a259a613bfd3a7e35e Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:34 +0100 Subject: linux-user: Support for restarting system calls for SPARC targets Update the SPARC main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * set all guest CPU state within signal.c code on sigreturn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-9-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Commit message tweaks; drop TARGET_USE_ERESTARTSYS define] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 3 +++ linux-user/signal.c | 2 +- linux-user/sparc/target_signal.h | 1 + linux-user/sparc64/target_signal.h | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index dfc098f..04a9b9b 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1376,6 +1376,9 @@ void cpu_loop (CPUSPARCState *env) env->regwptr[2], env->regwptr[3], env->regwptr[4], env->regwptr[5], 0, 0); + if (ret == -TARGET_ERESTARTSYS || ret == -TARGET_QEMU_ESIGRETURN) { + break; + } if ((abi_ulong)ret >= (abi_ulong)(-515)) { #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) env->xcc |= PSR_CARRY; diff --git a/linux-user/signal.c b/linux-user/signal.c index 14e58b0..e742347 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -2449,7 +2449,7 @@ long do_sigreturn(CPUSPARCState *env) goto segv_and_exit; } unlock_user_struct(sf, sf_addr, 0); - return env->regwptr[0]; + return -TARGET_QEMU_ESIGRETURN; segv_and_exit: unlock_user_struct(sf, sf_addr, 0); diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h index c7de300..2df38c8 100644 --- a/linux-user/sparc/target_signal.h +++ b/linux-user/sparc/target_signal.h @@ -33,4 +33,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) return state->regwptr[UREG_FP]; } + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/sparc64/target_signal.h b/linux-user/sparc64/target_signal.h index c7de300..2df38c8 100644 --- a/linux-user/sparc64/target_signal.h +++ b/linux-user/sparc64/target_signal.h @@ -33,4 +33,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) return state->regwptr[UREG_FP]; } + #endif /* TARGET_SIGNAL_H */ -- cgit v1.1 From ba41249678f8c1504bf07706ddb0eda0d36cccc2 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:35 +0100 Subject: linux-user: Support for restarting system calls for SH4 targets Update the SH4 main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * set all guest CPU state within signal.c code on sigreturn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-12-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: tweak commit message; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 6 +++++- linux-user/sh4/target_signal.h | 1 + linux-user/signal.c | 16 ++++++---------- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index 04a9b9b..75552a0 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2826,7 +2826,11 @@ void cpu_loop(CPUSH4State *env) env->gregs[0], env->gregs[1], 0, 0); - env->gregs[0] = ret; + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->gregs[0] = ret; + } break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h index e148da0..f9911aa 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state) return state->gregs[15]; } + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index e742347..8b5ddf2 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3202,13 +3202,12 @@ static void setup_sigcontext(struct target_sigcontext *sc, __put_user(mask, &sc->oldmask); } -static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc, - target_ulong *r0_p) +static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc) { int i; #define COPY(x) __get_user(regs->x, &sc->sc_##x) - COPY(gregs[1]); + COPY(gregs[0]); COPY(gregs[1]); COPY(gregs[2]); COPY(gregs[3]); COPY(gregs[4]); COPY(gregs[5]); COPY(gregs[6]); COPY(gregs[7]); @@ -3228,7 +3227,6 @@ static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc, __get_user(regs->fpul, &sc->sc_fpul); regs->tra = -1; /* disable syscall checks */ - __get_user(*r0_p, &sc->sc_gregs[0]); } static void setup_frame(int sig, struct target_sigaction *ka, @@ -3345,7 +3343,6 @@ long do_sigreturn(CPUSH4State *regs) abi_ulong frame_addr; sigset_t blocked; target_sigset_t target_set; - target_ulong r0; int i; int err = 0; @@ -3366,10 +3363,10 @@ long do_sigreturn(CPUSH4State *regs) target_to_host_sigset_internal(&blocked, &target_set); do_sigprocmask(SIG_SETMASK, &blocked, NULL); - restore_sigcontext(regs, &frame->sc, &r0); + restore_sigcontext(regs, &frame->sc); unlock_user_struct(frame, frame_addr, 0); - return r0; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); @@ -3382,7 +3379,6 @@ long do_rt_sigreturn(CPUSH4State *regs) struct target_rt_sigframe *frame; abi_ulong frame_addr; sigset_t blocked; - target_ulong r0; frame_addr = regs->gregs[15]; trace_user_do_rt_sigreturn(regs, frame_addr); @@ -3393,7 +3389,7 @@ long do_rt_sigreturn(CPUSH4State *regs) target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask); do_sigprocmask(SIG_SETMASK, &blocked, NULL); - restore_sigcontext(regs, &frame->uc.tuc_mcontext, &r0); + restore_sigcontext(regs, &frame->uc.tuc_mcontext); if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, uc.tuc_stack), @@ -3402,7 +3398,7 @@ long do_rt_sigreturn(CPUSH4State *regs) } unlock_user_struct(frame, frame_addr, 0); - return r0; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); -- cgit v1.1 From 338c858c946017cd3ec8c2be06d817e001d94bc3 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:36 +0100 Subject: linux-user: Support for restarting system calls for Alpha targets Update the Alpha main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-13-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: tweak commit message; drop TARGET_USE_ERESTARTSYS define; PC is env->pc, not env->ir[IR_PV]] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/alpha/target_signal.h | 1 + linux-user/main.c | 7 +++++-- linux-user/signal.c | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'linux-user') diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h index d3822da..4c78319 100644 --- a/linux-user/alpha/target_signal.h +++ b/linux-user/alpha/target_signal.h @@ -27,6 +27,7 @@ static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state) return state->ir[IR_SP]; } + /* From . */ #define TARGET_GEN_INTOVF -1 /* integer overflow */ #define TARGET_GEN_INTDIV -2 /* integer division by zero */ diff --git a/linux-user/main.c b/linux-user/main.c index 75552a0..cc7f2aa 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3267,8 +3267,11 @@ void cpu_loop(CPUAlphaState *env) env->ir[IR_A2], env->ir[IR_A3], env->ir[IR_A4], env->ir[IR_A5], 0, 0); - if (trapnr == TARGET_NR_sigreturn - || trapnr == TARGET_NR_rt_sigreturn) { + if (sysret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + break; + } + if (sysret == -TARGET_QEMU_ESIGRETURN) { break; } /* Syscall writes 0 to V0 to bypass error check, similar diff --git a/linux-user/signal.c b/linux-user/signal.c index 8b5ddf2..559e764 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -5527,7 +5527,7 @@ long do_sigreturn(CPUAlphaState *env) restore_sigcontext(env, sc); unlock_user_struct(sc, sc_addr, 0); - return env->ir[IR_V0]; + return -TARGET_QEMU_ESIGRETURN; badframe: force_sig(TARGET_SIGSEGV); @@ -5554,7 +5554,7 @@ long do_rt_sigreturn(CPUAlphaState *env) } unlock_user_struct(frame, frame_addr, 0); - return env->ir[IR_V0]; + return -TARGET_QEMU_ESIGRETURN; badframe: -- cgit v1.1 From 256cb6af7f04ae385883408084b3ef989e2423d8 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:37 +0100 Subject: linux-user: Support for restarting system calls for UniCore32 targets Update the UniCore32 main loop code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state (We don't support signals on this target so there is no sigreturn code to update.) Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-30-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: tweak commit message; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index cc7f2aa..9e6448a 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1171,7 +1171,7 @@ void cpu_loop(CPUUniCore32State *env) cpu_set_tls(env, env->regs[0]); env->regs[0] = 0; } else { - env->regs[0] = do_syscall(env, + abi_long ret = do_syscall(env, n, env->regs[0], env->regs[1], @@ -1180,6 +1180,11 @@ void cpu_loop(CPUUniCore32State *env) env->regs[4], env->regs[5], 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->regs[31] -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[0] = ret; + } } } else { goto error; -- cgit v1.1 From 7fe7231a4904529404e85517888112c0acc0de4e Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:38 +0100 Subject: linux-user: Support for restarting system calls for OpenRISC targets Update the OpenRISC main loop code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state (We don't implement sigreturn on this target so there is no code there to update.) Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-31-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: tweak commit message; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 22 ++++++++++++++-------- linux-user/openrisc/target_signal.h | 1 + 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index 9e6448a..6a0be7d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2724,6 +2724,7 @@ void cpu_loop(CPUOpenRISCState *env) { CPUState *cs = CPU(openrisc_env_get_cpu(env)); int trapnr, gdbsig; + abi_long ret; for (;;) { cpu_exec_start(cs); @@ -2769,14 +2770,19 @@ void cpu_loop(CPUOpenRISCState *env) break; case EXCP_SYSCALL: env->pc += 4; /* 0xc00; */ - env->gpr[11] = do_syscall(env, - env->gpr[11], /* return value */ - env->gpr[3], /* r3 - r7 are params */ - env->gpr[4], - env->gpr[5], - env->gpr[6], - env->gpr[7], - env->gpr[8], 0, 0); + ret = do_syscall(env, + env->gpr[11], /* return value */ + env->gpr[3], /* r3 - r7 are params */ + env->gpr[4], + env->gpr[5], + env->gpr[6], + env->gpr[7], + env->gpr[8], 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->gpr[11] = ret; + } break; case EXCP_FPE: qemu_log_mask(CPU_LOG_INT, "\nFloating point error\n"); diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index 964aed6..f600501 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -23,4 +23,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUOpenRISCState *state) return state->gpr[1]; } + #endif /* TARGET_SIGNAL_H */ -- cgit v1.1 From 7ccb84a91618eda626b12ce83d62cfe678cfc58f Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:39 +0100 Subject: linux-user: Support for restarting system calls for M68K targets Update the M68K main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * set all guest CPU state within signal.c code on sigreturn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-32-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: tweak commit message; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/m68k/target_signal.h | 1 + linux-user/main.c | 24 +++++++++++++++--------- linux-user/signal.c | 20 ++++++++------------ 3 files changed, 24 insertions(+), 21 deletions(-) (limited to 'linux-user') diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h index 479758a..9deaa89 100644 --- a/linux-user/m68k/target_signal.h +++ b/linux-user/m68k/target_signal.h @@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUM68KState *state) return state->aregs[7]; } + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/main.c b/linux-user/main.c index 6a0be7d..58dc91c 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3086,18 +3086,24 @@ void cpu_loop(CPUM68KState *env) break; case EXCP_TRAP0: { + abi_long ret; ts->sim_syscalls = 0; n = env->dregs[0]; env->pc += 2; - env->dregs[0] = do_syscall(env, - n, - env->dregs[1], - env->dregs[2], - env->dregs[3], - env->dregs[4], - env->dregs[5], - env->aregs[0], - 0, 0); + ret = do_syscall(env, + n, + env->dregs[1], + env->dregs[2], + env->dregs[3], + env->dregs[4], + env->dregs[5], + env->aregs[0], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->dregs[0] = ret; + } } break; case EXCP_INTERRUPT: diff --git a/linux-user/signal.c b/linux-user/signal.c index 559e764..3eea6b7 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -5009,19 +5009,18 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPUM68KState *env, } static void -restore_sigcontext(CPUM68KState *env, struct target_sigcontext *sc, int *pd0) +restore_sigcontext(CPUM68KState *env, struct target_sigcontext *sc) { int temp; __get_user(env->aregs[7], &sc->sc_usp); + __get_user(env->dregs[0], &sc->sc_d0); __get_user(env->dregs[1], &sc->sc_d1); __get_user(env->aregs[0], &sc->sc_a0); __get_user(env->aregs[1], &sc->sc_a1); __get_user(env->pc, &sc->sc_pc); __get_user(temp, &sc->sc_sr); env->sr = (env->sr & 0xff00) | (temp & 0xff); - - *pd0 = tswapl(sc->sc_d0); } /* @@ -5120,8 +5119,7 @@ static inline int target_rt_setup_ucontext(struct target_ucontext *uc, } static inline int target_rt_restore_ucontext(CPUM68KState *env, - struct target_ucontext *uc, - int *pd0) + struct target_ucontext *uc) { int temp; target_greg_t *gregs = uc->tuc_mcontext.gregs; @@ -5151,7 +5149,6 @@ static inline int target_rt_restore_ucontext(CPUM68KState *env, __get_user(temp, &gregs[17]); env->sr = (env->sr & 0xff00) | (temp & 0xff); - *pd0 = env->dregs[0]; return 0; badframe: @@ -5238,7 +5235,7 @@ long do_sigreturn(CPUM68KState *env) abi_ulong frame_addr = env->aregs[7] - 4; target_sigset_t target_set; sigset_t set; - int d0, i; + int i; trace_user_do_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) @@ -5257,10 +5254,10 @@ long do_sigreturn(CPUM68KState *env) /* restore registers */ - restore_sigcontext(env, &frame->sc, &d0); + restore_sigcontext(env, &frame->sc); unlock_user_struct(frame, frame_addr, 0); - return d0; + return -TARGET_QEMU_ESIGRETURN; badframe: force_sig(TARGET_SIGSEGV); @@ -5273,7 +5270,6 @@ long do_rt_sigreturn(CPUM68KState *env) abi_ulong frame_addr = env->aregs[7] - 4; target_sigset_t target_set; sigset_t set; - int d0; trace_user_do_rt_sigreturn(env, frame_addr); if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) @@ -5284,7 +5280,7 @@ long do_rt_sigreturn(CPUM68KState *env) /* restore registers */ - if (target_rt_restore_ucontext(env, &frame->uc, &d0)) + if (target_rt_restore_ucontext(env, &frame->uc)) goto badframe; if (do_sigaltstack(frame_addr + @@ -5293,7 +5289,7 @@ long do_rt_sigreturn(CPUM68KState *env) goto badframe; unlock_user_struct(frame, frame_addr, 0); - return d0; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); -- cgit v1.1 From 47405ab642101c8ea0472ae434ab4bd2bc1fa41f Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:40 +0100 Subject: linux-user: Support for restarting system calls for S390 targets Update the S390 main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * set all guest CPU state within signal.c code on sigreturn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-33-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell [PMM: tweak commit message; remove stray double semicolon; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 12 +++++++++--- linux-user/s390x/target_signal.h | 1 + linux-user/signal.c | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index 58dc91c..c16d7ed 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3385,6 +3385,7 @@ void cpu_loop(CPUS390XState *env) int trapnr, n, sig; target_siginfo_t info; target_ulong addr; + abi_long ret; while (1) { cpu_exec_start(cs); @@ -3402,9 +3403,14 @@ void cpu_loop(CPUS390XState *env) n = env->regs[1]; } env->psw.addr += env->int_svc_ilen; - env->regs[2] = do_syscall(env, n, env->regs[2], env->regs[3], - env->regs[4], env->regs[5], - env->regs[6], env->regs[7], 0, 0); + ret = do_syscall(env, n, env->regs[2], env->regs[3], + env->regs[4], env->regs[5], + env->regs[6], env->regs[7], 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->psw.addr -= env->int_svc_ilen; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[2] = ret; + } break; case EXCP_DEBUG: diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h index b4816b0..a6fb287 100644 --- a/linux-user/s390x/target_signal.h +++ b/linux-user/s390x/target_signal.h @@ -23,4 +23,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state) return state->regs[15]; } + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index 3eea6b7..51e11c1 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4280,7 +4280,7 @@ long do_sigreturn(CPUS390XState *env) } unlock_user_struct(frame, frame_addr, 0); - return env->regs[2]; + return -TARGET_QEMU_ESIGRETURN; badframe: force_sig(TARGET_SIGSEGV); @@ -4310,7 +4310,7 @@ long do_rt_sigreturn(CPUS390XState *env) goto badframe; } unlock_user_struct(frame, frame_addr, 0); - return env->regs[2]; + return -TARGET_QEMU_ESIGRETURN; badframe: unlock_user_struct(frame, frame_addr, 0); -- cgit v1.1 From 6205086558955402983f1c2ff9e4c3ebe9f1c678 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:41 +0100 Subject: linux-user: Support for restarting system calls for CRIS targets Update the CRIS main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * set all guest CPU state within signal.c code on sigreturn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-34-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Peter Maydell Reviewed-by: Edgar E. Iglesias [PMM: tweak commit message; drop TARGET_USE_ERESTARTSYS define] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/cris/target_signal.h | 1 + linux-user/main.c | 6 +++++- linux-user/signal.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'linux-user') diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h index 5611840..e0f1382 100644 --- a/linux-user/cris/target_signal.h +++ b/linux-user/cris/target_signal.h @@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state) return state->regs[14]; } + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/main.c b/linux-user/main.c index c16d7ed..a532221 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2914,7 +2914,11 @@ void cpu_loop(CPUCRISState *env) env->pregs[7], env->pregs[11], 0, 0); - env->regs[10] = ret; + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[10] = ret; + } break; case EXCP_DEBUG: { diff --git a/linux-user/signal.c b/linux-user/signal.c index 51e11c1..71a8e2a 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3785,7 +3785,7 @@ long do_sigreturn(CPUCRISState *env) restore_sigcontext(&frame->sc, env); unlock_user_struct(frame, frame_addr, 0); - return env->regs[10]; + return -TARGET_QEMU_ESIGRETURN; badframe: force_sig(TARGET_SIGSEGV); } -- cgit v1.1 From a9175169cc55ecff23a158dfee7d9cbb0b75d185 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 May 2016 18:47:42 +0100 Subject: linux-user: Support for restarting system calls for tilegx targets Update the tilegx main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * return -TARGET_QEMU_ESIGRETURN from sigreturn rather than current R_RE * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state Note that this fixes a bug where a sigreturn which happened to have an errno value in TILEGX_R_RE would incorrectly cause TILEGX_R_ERR to get set. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 21 +++++++++++++-------- linux-user/signal.c | 2 +- linux-user/tilegx/target_signal.h | 1 + 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index a532221..4607e48 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3706,15 +3706,20 @@ void cpu_loop(CPUTLGState *env) cpu_exec_end(cs); switch (trapnr) { case TILEGX_EXCP_SYSCALL: - env->regs[TILEGX_R_RE] = do_syscall(env, env->regs[TILEGX_R_NR], - env->regs[0], env->regs[1], - env->regs[2], env->regs[3], - env->regs[4], env->regs[5], - env->regs[6], env->regs[7]); - env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(env->regs[TILEGX_R_RE]) - ? - env->regs[TILEGX_R_RE] - : 0; + { + abi_ulong ret = do_syscall(env, env->regs[TILEGX_R_NR], + env->regs[0], env->regs[1], + env->regs[2], env->regs[3], + env->regs[4], env->regs[5], + env->regs[6], env->regs[7]); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 8; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[TILEGX_R_RE] = ret; + env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(ret) ? -ret : 0; + } break; + } case TILEGX_EXCP_OPCODE_EXCH: do_exch(env, true, false); break; diff --git a/linux-user/signal.c b/linux-user/signal.c index 71a8e2a..b4641df 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -5709,7 +5709,7 @@ long do_rt_sigreturn(CPUTLGState *env) } unlock_user_struct(frame, frame_addr, 0); - return env->regs[TILEGX_R_RE]; + return -TARGET_QEMU_ESIGRETURN; badframe: diff --git a/linux-user/tilegx/target_signal.h b/linux-user/tilegx/target_signal.h index b595f98..fcf1040 100644 --- a/linux-user/tilegx/target_signal.h +++ b/linux-user/tilegx/target_signal.h @@ -25,4 +25,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUTLGState *state) return state->regs[TILEGX_R_SP]; } + #endif /* TARGET_SIGNAL_H */ -- cgit v1.1 From d7749ab770601258be7ae862b5827c42bb35e44c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 May 2016 18:47:43 +0100 Subject: linux-user: Set r14 on exit from microblaze syscall All syscall exits on microblaze result in r14 being equal to the PC we return to, because the kernel syscall exit instruction "rtbd" does this. (This is true even for sigreturn(); note that r14 is not a userspace-usable register as the kernel may clobber it at any point.) Emulate the setting of r14 on exit; this isn't really a guest visible change for valid guest code because r14 isn't reliably observable anyway. However having the code and the comment helps to explain why it's ok for the ERESTARTSYS handling not to undo the changes to r14 that happen on syscall entry. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index 4607e48..c5da418 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2983,6 +2983,13 @@ void cpu_loop(CPUMBState *env) env->regs[10], 0, 0); env->regs[3] = ret; + /* All syscall exits result in guest r14 being equal to the + * PC we return to, because the kernel syscall exit "rtbd" does + * this. (This is true even for sigreturn(); note that r14 is + * not a userspace-usable register, as the kernel may clobber it + * at any point.) + */ + env->regs[14] = env->sregs[SR_PC]; break; case EXCP_HW_EXCP: env->regs[17] = env->sregs[SR_PC] + 4; -- cgit v1.1 From 4134ecfeb903c362558cb1cb594ff532fd83fb84 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:44 +0100 Subject: linux-user: Support for restarting system calls for Microblaze targets Update the Microblaze main loop and sigreturn code: * on TARGET_ERESTARTSYS, wind guest PC backwards to repeat syscall insn * set all guest CPU state within signal.c code on sigreturn * handle TARGET_QEMU_ESIGRETURN in the main loop as the indication that the main loop should not touch any guest CPU state Note that this in passing fixes a bug where we were corrupting the guest r[3] on sigreturn with the guest's r[10] because do_sigreturn() was returning env->regs[10] but the register for syscall return values is env->regs[3]. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-11-git-send-email-T.E.Baldwin99@members.leeds.ac.uk Reviewed-by: Edgar E. Iglesias Reviewed-by: Peter Maydell [PMM: Commit message tweaks; drop TARGET_USE_ERESTARTSYS define; drop whitespace changes] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/main.c | 7 ++++++- linux-user/microblaze/target_signal.h | 1 + linux-user/signal.c | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'linux-user') diff --git a/linux-user/main.c b/linux-user/main.c index c5da418..b2bc6ab 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2982,7 +2982,12 @@ void cpu_loop(CPUMBState *env) env->regs[9], env->regs[10], 0, 0); - env->regs[3] = ret; + if (ret == -TARGET_ERESTARTSYS) { + /* Wind back to before the syscall. */ + env->sregs[SR_PC] -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[3] = ret; + } /* All syscall exits result in guest r14 being equal to the * PC we return to, because the kernel syscall exit "rtbd" does * this. (This is true even for sigreturn(); note that r14 is diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h index 3d1f7a7..acdf3b5 100644 --- a/linux-user/microblaze/target_signal.h +++ b/linux-user/microblaze/target_signal.h @@ -26,4 +26,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUMBState *state) return state->regs[14]; } + #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/signal.c b/linux-user/signal.c index b4641df..9e85550 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3618,7 +3618,7 @@ long do_sigreturn(CPUMBState *env) env->regs[14] = env->sregs[SR_PC]; unlock_user_struct(frame, frame_addr, 0); - return env->regs[10]; + return -TARGET_QEMU_ESIGRETURN; badframe: force_sig(TARGET_SIGSEGV); } -- cgit v1.1 From 71a8f7fece3e42dc55e865e081866f62f5c8c07e Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:45 +0100 Subject: linux-user: Add debug code to exercise restarting system calls If DEBUG_ERESTARTSYS is set restart all system calls once. This is pure debug code for exercising the syscall restart code paths in the per-architecture cpu main loops. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-10-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Add comment and a commented-out #define next to the commented-out generic DEBUG #define; remove the check on TARGET_USE_ERESTARTSYS; tweak comment message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index a4a1af7..ced519d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -110,6 +110,10 @@ int __clone2(int (*fn)(void *), void *child_stack_base, CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID) //#define DEBUG +/* Define DEBUG_ERESTARTSYS to force every syscall to be restarted + * once. This exercises the codepaths for restart. + */ +//#define DEBUG_ERESTARTSYS //#include #define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct linux_dirent [2]) @@ -5871,6 +5875,21 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct statfs stfs; void *p; +#if defined(DEBUG_ERESTARTSYS) + /* Debug-only code for exercising the syscall-restart code paths + * in the per-architecture cpu main loops: restart every syscall + * the guest makes once before letting it through. + */ + { + static int flag; + + flag = !flag; + if (flag) { + return -TARGET_ERESTARTSYS; + } + } +#endif + #ifdef DEBUG gemu_log("syscall %d", num); #endif -- cgit v1.1 From 4d330cee37a21aabfc619a1948953559e66951a4 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:46 +0100 Subject: linux-user: Provide safe_syscall for fixing races between signals and syscalls If a signal is delivered immediately before a blocking system call the handler will only be called after the system call returns, which may be a long time later or never. This is fixed by using a function (safe_syscall) that checks if a guest signal is pending prior to making a system call, and if so does not call the system call and returns -TARGET_ERESTARTSYS. If a signal is received between the check and the system call host_signal_handler() rewinds execution to before the check. This rewinding has the effect of closing the race window so that safe_syscall will reliably either (a) go into the host syscall with no unprocessed guest signals pending or or (b) return -TARGET_ERESTARTSYS so that the caller can deal with the signals. Implementing this requires a per-host-architecture assembly language fragment. This will also resolve the mishandling of the SA_RESTART flag where we would restart a host system call and not call the guest signal handler until the syscall finally completed -- syscall restarting now always happens at the guest syscall level so the guest signal handler will run. (The host syscall will never be restarted because if the host kernel rewinds the PC to point at the syscall insn for a restart then our host_signal_handler() will see this and arrange the guest PC rewind.) This commit contains the infrastructure for implementing safe_syscall and the assembly language fragment for x86-64, but does not change any syscalls to use it. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-14-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: * Avoid having an architecture if-ladder in configure by putting linux-user/host/$(ARCH) on the include path and including safe-syscall.inc.S from it * Avoid ifdef ladder in signal.c by creating new hostdep.h to hold host-architecture-specific things * Added copyright/license header to safe-syscall.inc.S * Rewrote commit message * Added comments to safe-syscall.inc.S * Changed calling convention of safe_syscall() to match syscall() (returns -1 and host error in errno on failure) * Added a long comment in qemu.h about how to use safe_syscall() to implement guest syscalls. ] RV: squashed Peters "fixup! linux-user: compile on non-x86-64 hosts" patch Signed-off-by: Peter Maydell --- linux-user/Makefile.objs | 3 +- linux-user/host/generic/hostdep.h | 20 +++++ linux-user/host/x86_64/hostdep.h | 38 +++++++++ linux-user/host/x86_64/safe-syscall.inc.S | 81 +++++++++++++++++++ linux-user/qemu.h | 127 +++++++++++++++++++++++++++++- linux-user/safe-syscall.S | 30 +++++++ linux-user/signal.c | 10 +++ linux-user/syscall.c | 47 +++++++++++ 8 files changed, 354 insertions(+), 2 deletions(-) create mode 100644 linux-user/host/generic/hostdep.h create mode 100644 linux-user/host/x86_64/hostdep.h create mode 100644 linux-user/host/x86_64/safe-syscall.inc.S create mode 100644 linux-user/safe-syscall.S (limited to 'linux-user') diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index fd50217..8c93058 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -1,5 +1,6 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \ - elfload.o linuxload.o uaccess.o uname.o + elfload.o linuxload.o uaccess.o uname.o \ + safe-syscall.o obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o diff --git a/linux-user/host/generic/hostdep.h b/linux-user/host/generic/hostdep.h new file mode 100644 index 0000000..cfabc35 --- /dev/null +++ b/linux-user/host/generic/hostdep.h @@ -0,0 +1,20 @@ +/* + * hostdep.h : fallback generic version of header for things + * which are dependent on the host architecture + * + * * Written by Peter Maydell + * + * 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. + */ + +#ifndef QEMU_HOSTDEP_H +#define QEMU_HOSTDEP_H + +/* This is the fallback header which is only used if the host + * architecture doesn't provide one in linux-user/host/$ARCH. + */ + +#endif diff --git a/linux-user/host/x86_64/hostdep.h b/linux-user/host/x86_64/hostdep.h new file mode 100644 index 0000000..9dfbf3a --- /dev/null +++ b/linux-user/host/x86_64/hostdep.h @@ -0,0 +1,38 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell + * + * 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. + */ + +#ifndef QEMU_HOSTDEP_H +#define QEMU_HOSTDEP_H + +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#ifndef __ASSEMBLER__ + +/* These are defined by the safe-syscall.inc.S file */ +extern char safe_syscall_start[]; +extern char safe_syscall_end[]; + +/* Adjust the signal context to rewind out of safe-syscall if we're in it */ +static inline void rewind_if_in_safe_syscall(void *puc) +{ + struct ucontext *uc = puc; + greg_t *pcreg = &uc->uc_mcontext.gregs[REG_RIP]; + + if (*pcreg > (uintptr_t)safe_syscall_start + && *pcreg < (uintptr_t)safe_syscall_end) { + *pcreg = (uintptr_t)safe_syscall_start; + } +} + +#endif /* __ASSEMBLER__ */ + +#endif diff --git a/linux-user/host/x86_64/safe-syscall.inc.S b/linux-user/host/x86_64/safe-syscall.inc.S new file mode 100644 index 0000000..dde434c --- /dev/null +++ b/linux-user/host/x86_64/safe-syscall.inc.S @@ -0,0 +1,81 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * Copyright (C) 2015 Timothy Edward Baldwin + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + + /* This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +safe_syscall_base: + /* 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 + + /* 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 */ + testl $1, (%rbp) + jnz return_ERESTARTSYS + syscall +safe_syscall_end: + /* code path for having successfully executed the syscall */ + pop %rbp + ret + +return_ERESTARTSYS: + /* code path when we didn't execute the syscall */ + mov $-TARGET_ERESTARTSYS, %rax + pop %rbp + ret + + .size safe_syscall_base, .-safe_syscall_base diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 208c63e..f09b750 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -1,7 +1,7 @@ #ifndef QEMU_H #define QEMU_H - +#include "hostdep.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" @@ -205,6 +205,131 @@ unsigned long init_guest_space(unsigned long host_start, #include "qemu/log.h" +/* safe_syscall.S */ + +/** + * safe_syscall: + * @int number: number of system call to make + * ...: arguments to the system call + * + * Call a system call if guest signal not pending. + * This has the same API as the libc syscall() function, except that it + * may return -1 with errno == TARGET_ERESTARTSYS if a signal was pending. + * + * Returns: the system call result, or -1 with an error code in errno + * (Errnos are host errnos; we rely on TARGET_ERESTARTSYS not clashing + * with any of the host errno values.) + */ + +/* A guide to using safe_syscall() to handle interactions between guest + * syscalls and guest signals: + * + * Guest syscalls come in two flavours: + * + * (1) Non-interruptible syscalls + * + * These are guest syscalls that never get interrupted by signals and + * so never return EINTR. They can be implemented straightforwardly in + * QEMU: just make sure that if the implementation code has to make any + * blocking calls that those calls are retried if they return EINTR. + * It's also OK to implement these with safe_syscall, though it will be + * a little less efficient if a signal is delivered at the 'wrong' moment. + * + * (2) Interruptible syscalls + * + * These are guest syscalls that can be interrupted by signals and + * for which we need to either return EINTR or arrange for the guest + * syscall to be restarted. This category includes both syscalls which + * always restart (and in the kernel return -ERESTARTNOINTR), ones + * which only restart if there is no handler (kernel returns -ERESTARTNOHAND + * or -ERESTART_RESTARTBLOCK), and the most common kind which restart + * if the handler was registered with SA_RESTART (kernel returns + * -ERESTARTSYS). System calls which are only interruptible in some + * situations (like 'open') also need to be handled this way. + * + * Here it is important that the host syscall is made + * via this safe_syscall() function, and *not* via the host libc. + * If the host libc is used then the implementation will appear to work + * most of the time, but there will be a race condition where a + * signal could arrive just before we make the host syscall inside libc, + * and then then guest syscall will not correctly be interrupted. + * Instead the implementation of the guest syscall can use the safe_syscall + * function but otherwise just return the result or errno in the usual + * way; the main loop code will take care of restarting the syscall + * if appropriate. + * + * (If the implementation needs to make multiple host syscalls this is + * OK; any which might really block must be via safe_syscall(); for those + * which are only technically blocking (ie which we know in practice won't + * stay in the host kernel indefinitely) it's OK to use libc if necessary. + * You must be able to cope with backing out correctly if some safe_syscall + * you make in the implementation returns either -TARGET_ERESTARTSYS or + * EINTR though.) + * + * + * How and why the safe_syscall implementation works: + * + * The basic setup is that we make the host syscall via a known + * section of host native assembly. If a signal occurs, our signal + * handler checks the interrupted host PC against the addresse of that + * known section. If the PC is before or at the address of the syscall + * instruction then we change the PC to point at a "return + * -TARGET_ERESTARTSYS" code path instead, and then exit the signal handler + * (causing the safe_syscall() call to immediately return that value). + * Then in the main.c loop if we see this magic return value we adjust + * the guest PC to wind it back to before the system call, and invoke + * the guest signal handler as usual. + * + * This winding-back will happen in two cases: + * (1) signal came in just before we took the host syscall (a race); + * in this case we'll take the guest signal and have another go + * at the syscall afterwards, and this is indistinguishable for the + * guest from the timing having been different such that the guest + * signal really did win the race + * (2) signal came in while the host syscall was blocking, and the + * host kernel decided the syscall should be restarted; + * in this case we want to restart the guest syscall also, and so + * rewinding is the right thing. (Note that "restart" semantics mean + * "first call the signal handler, then reattempt the syscall".) + * The other situation to consider is when a signal came in while the + * host syscall was blocking, and the host kernel decided that the syscall + * should not be restarted; in this case QEMU's host signal handler will + * be invoked with the PC pointing just after the syscall instruction, + * with registers indicating an EINTR return; the special code in the + * handler will not kick in, and we will return EINTR to the guest as + * we should. + * + * Notice that we can leave the host kernel to make the decision for + * us about whether to do a restart of the syscall or not; we do not + * need to check SA_RESTART flags in QEMU or distinguish the various + * kinds of restartability. + */ +#ifdef HAVE_SAFE_SYSCALL +/* The core part of this function is implemented in assembly */ +extern long safe_syscall_base(int *pending, long number, ...); + +#define safe_syscall(...) \ + ({ \ + long ret_; \ + int *psp_ = &((TaskState *)thread_cpu->opaque)->signal_pending; \ + ret_ = safe_syscall_base(psp_, __VA_ARGS__); \ + if (is_error(ret_)) { \ + errno = -ret_; \ + ret_ = -1; \ + } \ + ret_; \ + }) + +#else + +/* Fallback for architectures which don't yet provide a safe-syscall assembly + * fragment; note that this is racy! + * This should go away when all host architectures have been updated. + */ +#define safe_syscall syscall + +#endif + /* syscall.c */ int host_to_target_waitstatus(int status); diff --git a/linux-user/safe-syscall.S b/linux-user/safe-syscall.S new file mode 100644 index 0000000..b5df625 --- /dev/null +++ b/linux-user/safe-syscall.S @@ -0,0 +1,30 @@ +/* + * safe-syscall.S : include the host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * + * Written by Peter Maydell + * + * 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 "hostdep.h" +#include "errno_defs.h" + +/* We have the correct host directory on our include path + * so that this will pull in the right fragment for the architecture. + */ +#ifdef HAVE_SAFE_SYSCALL +#include "safe-syscall.inc.S" +#endif + +/* 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(__linux__) && defined(__ELF__) +.section .note.GNU-stack, "", %progbits +#endif diff --git a/linux-user/signal.c b/linux-user/signal.c index 9e85550..ff4de4f 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -561,6 +561,13 @@ int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) } } +#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) { @@ -581,6 +588,9 @@ static void host_signal_handler(int host_signum, siginfo_t *info, 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); if (queue_signal(env, sig, &tinfo) == 1) { /* interrupt the virtual CPU as soon as possible */ diff --git a/linux-user/syscall.c b/linux-user/syscall.c index ced519d..b6a8ed6 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -660,6 +660,53 @@ char *target_strerror(int err) return strerror(target_to_host_errno(err)); } +#define safe_syscall0(type, name) \ +static type safe_##name(void) \ +{ \ + return safe_syscall(__NR_##name); \ +} + +#define safe_syscall1(type, name, type1, arg1) \ +static type safe_##name(type1 arg1) \ +{ \ + return safe_syscall(__NR_##name, arg1); \ +} + +#define safe_syscall2(type, name, type1, arg1, type2, arg2) \ +static type safe_##name(type1 arg1, type2 arg2) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2); \ +} + +#define safe_syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3); \ +} + +#define safe_syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4); \ +} + +#define safe_syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5); \ +} + +#define safe_syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ +static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) \ +{ \ + return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \ +} + static inline int host_to_target_sock_type(int host_type) { int target_type; -- cgit v1.1 From 50afd02b841cf0ccbc988a0fa868bbc4ca67c09e Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:47 +0100 Subject: linux-user: Use safe_syscall for read and write system calls Restart read() and write() if signals occur before, or during with SA_RESTART Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-15-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Update to new safe_syscall() convention of setting errno] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b6a8ed6..bee1360 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -707,6 +707,9 @@ static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ return safe_syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6); \ } +safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count) +safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count) + static inline int host_to_target_sock_type(int host_type) { int target_type; @@ -5983,7 +5986,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, else { if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) goto efault; - ret = get_errno(read(arg1, p, arg3)); + ret = get_errno(safe_read(arg1, p, arg3)); if (ret >= 0 && fd_trans_host_to_target_data(arg1)) { ret = fd_trans_host_to_target_data(arg1)(p, ret); @@ -5994,7 +5997,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_write: if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) goto efault; - ret = get_errno(write(arg1, p, arg3)); + ret = get_errno(safe_write(arg1, p, arg3)); unlock_user(p, arg2, 0); break; #ifdef TARGET_NR_open -- cgit v1.1 From c10a07387b77b94d8f7233f3b5bb559211d4e49a Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:48 +0100 Subject: linux-user: Use safe_syscall for open and openat system calls Restart open() and openat() if signals occur before, or during with SA_RESTART. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-17-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Adjusted to follow new -1-and-set-errno safe_syscall convention] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index bee1360..0037ee7 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -359,18 +359,6 @@ static int sys_getcwd1(char *buf, size_t size) return strlen(buf)+1; } -static int sys_openat(int dirfd, const char *pathname, int flags, mode_t mode) -{ - /* - * open(2) has extra parameter 'mode' when called with - * flag O_CREAT. - */ - if ((flags & O_CREAT) != 0) { - return (openat(dirfd, pathname, flags, mode)); - } - return (openat(dirfd, pathname, flags)); -} - #ifdef TARGET_NR_utimensat #ifdef CONFIG_UTIMENSAT static int sys_utimensat(int dirfd, const char *pathname, @@ -709,6 +697,8 @@ static type safe_##name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count) safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count) +safe_syscall4(int, openat, int, dirfd, const char *, pathname, \ + int, flags, mode_t, mode) static inline int host_to_target_sock_type(int host_type) { @@ -5851,7 +5841,7 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, if (is_proc_myself(pathname, "exe")) { int execfd = qemu_getauxval(AT_EXECFD); - return execfd ? execfd : sys_openat(dirfd, exec_path, flags, mode); + return execfd ? execfd : safe_openat(dirfd, exec_path, flags, mode); } for (fake_open = fakes; fake_open->filename; fake_open++) { @@ -5887,7 +5877,7 @@ static int do_openat(void *cpu_env, int dirfd, const char *pathname, int flags, return fd; } - return sys_openat(dirfd, path(pathname), flags, mode); + return safe_openat(dirfd, path(pathname), flags, mode); } #define TIMER_MAGIC 0x0caf0000 -- cgit v1.1 From 4af80a3783950380df85ecca78aea3e3bad2e846 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:49 +0100 Subject: linux-user: Use safe_syscall for wait system calls Use safe_syscall for waitpid, waitid and wait4 syscalls. Note that this change allows us to implement support for waitid's fifth (rusage) argument in future; for the moment we ignore it as we have done up til now. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-18-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: Adjust to new safe_syscall convention. Add fifth waitid syscall argument (which isn't present in the libc interface but is in the syscall ABI)] Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 0037ee7..d9f4695 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -699,6 +699,10 @@ safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count) safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count) safe_syscall4(int, openat, int, dirfd, const char *, pathname, \ int, flags, mode_t, mode) +safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \ + struct rusage *, rusage) +safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \ + int, options, struct rusage *, rusage) static inline int host_to_target_sock_type(int host_type) { @@ -6037,7 +6041,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_NR_waitpid: { int status; - ret = get_errno(waitpid(arg1, &status, arg3)); + ret = get_errno(safe_wait4(arg1, &status, arg3, 0)); if (!is_error(ret) && arg2 && ret && put_user_s32(host_to_target_waitstatus(status), arg2)) goto efault; @@ -6049,7 +6053,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { siginfo_t info; info.si_pid = 0; - ret = get_errno(waitid(arg1, arg2, &info, arg4)); + ret = get_errno(safe_waitid(arg1, arg2, &info, arg4, NULL)); if (!is_error(ret) && arg3 && info.si_pid != 0) { if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_siginfo_t), 0))) goto efault; @@ -7761,7 +7765,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, rusage_ptr = &rusage; else rusage_ptr = NULL; - ret = get_errno(wait4(arg1, &status, arg3, rusage_ptr)); + ret = get_errno(safe_wait4(arg1, &status, arg3, rusage_ptr)); if (!is_error(ret)) { if (status_ptr && ret) { status = host_to_target_waitstatus(status); -- cgit v1.1 From ffdcbe223d23461669869e85786145cce65e1e8c Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Thu, 12 May 2016 18:47:50 +0100 Subject: linux-user: Use safe_syscall for execve syscall Wrap execve() in the safe-syscall handling. Although execve() is not an interruptible syscall, it is a special case: if we allow a signal to happen before we make the host$ syscall then we will 'lose' it, because at the point of execve the process leaves QEMU's control. So we use the safe syscall wrapper to ensure that we either take the signal as a guest signal, or else it does not happen before the execve completes and makes it the other program's problem. The practical upshot is that without this SIGTERM could fail to terminate the process. Signed-off-by: Timothy Edward Baldwin Message-id: 1441497448-32489-25-git-send-email-T.E.Baldwin99@members.leeds.ac.uk [PMM: expanded commit message to explain in more detail why this is needed, and add comment about it too] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d9f4695..dea827f 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -703,6 +703,7 @@ safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \ struct rusage *, rusage) safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \ int, options, struct rusage *, rusage) +safe_syscall3(int, execve, const char *, filename, char **, argv, char **, envp) static inline int host_to_target_sock_type(int host_type) { @@ -6179,7 +6180,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user_string(arg1))) goto execve_efault; - ret = get_errno(execve(p, argp, envp)); + /* Although execve() is not an interruptible syscall it is + * a special case where we must use the safe_syscall wrapper: + * if we allow a signal to happen before we make the host + * syscall then we will 'lose' it, because at the point of + * execve the process leaves QEMU's control. So we use the + * safe syscall wrapper to ensure that we either take the + * signal as a guest signal, or else it does not happen + * before the execve completes and makes it the other + * program's problem. + */ + ret = get_errno(safe_execve(p, argp, envp)); unlock_user(p, arg1, 0); goto execve_end; -- cgit v1.1 From 6df9d38d33be8397bda8838dea5a4a10d662749b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 May 2016 18:47:51 +0100 Subject: linux-user: Use safe_syscall for pselect, select syscalls Use the safe_syscall wrapper for the pselect and select syscalls. Since not every architecture has the select syscall, we now have to implement select in terms of pselect, which means doing timeval<->timespec conversion. (Five years on from the initial patch that added pselect support to QEMU and a decade after pselect6 went into the kernel, it seems safe to not try to support hosts with header files which don't define __NR_pselect6.) Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index dea827f..c9c2ae9 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -430,15 +430,6 @@ _syscall5(int, sys_ppoll, struct pollfd *, fds, nfds_t, nfds, size_t, sigsetsize) #endif -#if defined(TARGET_NR_pselect6) -#ifndef __NR_pselect6 -# define __NR_pselect6 -1 -#endif -#define __NR_sys_pselect6 __NR_pselect6 -_syscall6(int, sys_pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, - fd_set *, exceptfds, struct timespec *, timeout, void *, sig); -#endif - #if defined(TARGET_NR_prlimit64) #ifndef __NR_prlimit64 # define __NR_prlimit64 -1 @@ -704,6 +695,8 @@ safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \ safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \ int, options, struct rusage *, rusage) safe_syscall3(int, execve, const char *, filename, char **, argv, char **, envp) +safe_syscall6(int, pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, \ + fd_set *, exceptfds, struct timespec *, timeout, void *, sig) static inline int host_to_target_sock_type(int host_type) { @@ -1115,7 +1108,8 @@ static abi_long do_select(int n, { fd_set rfds, wfds, efds; fd_set *rfds_ptr, *wfds_ptr, *efds_ptr; - struct timeval tv, *tv_ptr; + struct timeval tv; + struct timespec ts, *ts_ptr; abi_long ret; ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); @@ -1134,12 +1128,15 @@ static abi_long do_select(int n, if (target_tv_addr) { if (copy_from_user_timeval(&tv, target_tv_addr)) return -TARGET_EFAULT; - tv_ptr = &tv; + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + ts_ptr = &ts; } else { - tv_ptr = NULL; + ts_ptr = NULL; } - ret = get_errno(select(n, rfds_ptr, wfds_ptr, efds_ptr, tv_ptr)); + ret = get_errno(safe_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr, + ts_ptr, NULL)); if (!is_error(ret)) { if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) @@ -1149,8 +1146,13 @@ static abi_long do_select(int n, if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) return -TARGET_EFAULT; - if (target_tv_addr && copy_to_user_timeval(target_tv_addr, &tv)) - return -TARGET_EFAULT; + if (target_tv_addr) { + tv.tv_sec = ts.tv_sec; + tv.tv_usec = ts.tv_nsec / 1000; + if (copy_to_user_timeval(target_tv_addr, &tv)) { + return -TARGET_EFAULT; + } + } } return ret; @@ -7206,8 +7208,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, sig_ptr = NULL; } - ret = get_errno(sys_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr, - ts_ptr, sig_ptr)); + ret = get_errno(safe_pselect6(n, rfds_ptr, wfds_ptr, efds_ptr, + ts_ptr, sig_ptr)); if (!is_error(ret)) { if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) -- cgit v1.1 From d509eeb13c9c6fef4a29ca43c64f591d8c61d201 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 12 May 2016 18:47:52 +0100 Subject: linux-user: Use safe_syscall for futex syscall Use the safe_syscall wrapper for the futex syscall. In particular, this fixes hangs when using programs that link against the Boehm garbage collector, including the Mono runtime. (We don't change the sys_futex() call in the implementation of the exit syscall, because as the FIXME comment there notes that should be handled by disabling signals, since we can't easily back out if the futex were to return ERESTARTSYS.) Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c9c2ae9..4e419fb 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -697,6 +697,8 @@ safe_syscall5(int, waitid, idtype_t, idtype, id_t, id, siginfo_t *, infop, \ safe_syscall3(int, execve, const char *, filename, char **, argv, char **, envp) safe_syscall6(int, pselect6, int, nfds, fd_set *, readfds, fd_set *, writefds, \ fd_set *, exceptfds, struct timespec *, timeout, void *, sig) +safe_syscall6(int,futex,int *,uaddr,int,op,int,val, \ + const struct timespec *,timeout,int *,uaddr2,int,val3) static inline int host_to_target_sock_type(int host_type) { @@ -5381,12 +5383,12 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, } else { pts = NULL; } - return get_errno(sys_futex(g2h(uaddr), op, tswap32(val), + return get_errno(safe_futex(g2h(uaddr), op, tswap32(val), pts, NULL, val3)); case FUTEX_WAKE: - return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0)); + return get_errno(safe_futex(g2h(uaddr), op, val, NULL, NULL, 0)); case FUTEX_FD: - return get_errno(sys_futex(g2h(uaddr), op, val, NULL, NULL, 0)); + return get_errno(safe_futex(g2h(uaddr), op, val, NULL, NULL, 0)); case FUTEX_REQUEUE: case FUTEX_CMP_REQUEUE: case FUTEX_WAKE_OP: @@ -5396,11 +5398,11 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, to satisfy the compiler. We do not need to tswap TIMEOUT since it's not compared to guest memory. */ pts = (struct timespec *)(uintptr_t) timeout; - return get_errno(sys_futex(g2h(uaddr), op, val, pts, - g2h(uaddr2), - (base_op == FUTEX_CMP_REQUEUE - ? tswap32(val3) - : val3))); + return get_errno(safe_futex(g2h(uaddr), op, val, pts, + g2h(uaddr2), + (base_op == FUTEX_CMP_REQUEUE + ? tswap32(val3) + : val3))); default: return -TARGET_ENOSYS; } -- cgit v1.1 From c7e35da348e2e4df072e6979c48fa5283e07d1db Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 19 May 2016 12:01:40 +0100 Subject: linux-user: Handle negative values in timespec conversion In a struct timespec, both fields are signed longs. Converting them from guest to host with code like host_ts->tv_sec = tswapal(target_ts->tv_sec); mishandles negative values if the guest has 32-bit longs and the host has 64-bit longs because tswapal()'s return type is abi_ulong: the assignment will zero-extend into the host long type rather than sign-extending it. Make the conversion routines use __get_user() and __set_user() instead: this automatically picks up the signedness of the field type and does the correct kind of sign or zero extension. It also handles the possibility that the target struct is not sufficiently aligned for the host's requirements. In particular, this fixes a hang when running the Linux Test Project mq_timedsend01 and mq_timedreceive01 tests: one of the test cases sets the timeout to -1 and expects an EINVAL failure, but we were setting a very long timeout instead. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 4e419fb..6c4f5c6 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5194,8 +5194,8 @@ static inline abi_long target_to_host_timespec(struct timespec *host_ts, if (!lock_user_struct(VERIFY_READ, target_ts, target_addr, 1)) return -TARGET_EFAULT; - host_ts->tv_sec = tswapal(target_ts->tv_sec); - host_ts->tv_nsec = tswapal(target_ts->tv_nsec); + __get_user(host_ts->tv_sec, &target_ts->tv_sec); + __get_user(host_ts->tv_nsec, &target_ts->tv_nsec); unlock_user_struct(target_ts, target_addr, 0); return 0; } @@ -5207,8 +5207,8 @@ static inline abi_long host_to_target_timespec(abi_ulong target_addr, if (!lock_user_struct(VERIFY_WRITE, target_ts, target_addr, 0)) return -TARGET_EFAULT; - target_ts->tv_sec = tswapal(host_ts->tv_sec); - target_ts->tv_nsec = tswapal(host_ts->tv_nsec); + __put_user(host_ts->tv_sec, &target_ts->tv_sec); + __put_user(host_ts->tv_nsec, &target_ts->tv_nsec); unlock_user_struct(target_ts, target_addr, 1); return 0; } -- cgit v1.1 From 99874f65526ed7827202c6e17c62f30d47652bdd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 May 2016 19:00:56 +0100 Subject: linux-user: Handle msgrcv error case correctly The msgrcv ABI is a bit odd -- the msgsz argument is a size_t, which is unsigned, but it must fail EINVAL if the value is negative when cast to a long. We were incorrectly passing the value through an "unsigned int", which meant that if the guest was 32-bit longs and the host was 64-bit longs an input of 0xffffffff (which should trigger EINVAL) would simply be passed to the host msgrcv() as 0xffffffff, where it does not cause the host kernel to reject it. Follow the same approach as do_msgsnd() in using a ssize_t and doing the check for negative values by hand, so we correctly fail in this corner case. This fixes the msgrcv03 Linux Test Project test case, which otherwise hangs. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 6c4f5c6..cec5b80 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3152,7 +3152,7 @@ static inline abi_long do_msgsnd(int msqid, abi_long msgp, } static inline abi_long do_msgrcv(int msqid, abi_long msgp, - unsigned int msgsz, abi_long msgtyp, + ssize_t msgsz, abi_long msgtyp, int msgflg) { struct target_msgbuf *target_mb; @@ -3160,6 +3160,10 @@ static inline abi_long do_msgrcv(int msqid, abi_long msgp, struct msgbuf *host_mb; abi_long ret = 0; + if (msgsz < 0) { + return -TARGET_EINVAL; + } + if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0)) return -TARGET_EFAULT; -- cgit v1.1 From 415d847110e3f8cd176160b92a5fdc56d8a20792 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 20 May 2016 19:00:57 +0100 Subject: linux-user: Use g_try_malloc() in do_msgrcv() In do_msgrcv() we want to allocate a message buffer, whose size is passed to us by the guest. That means we could legitimately fail, so use g_try_malloc() and handle the error case, in the same way that do_msgsnd() does. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index cec5b80..40e8742 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -3167,7 +3167,11 @@ static inline abi_long do_msgrcv(int msqid, abi_long msgp, if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0)) return -TARGET_EFAULT; - host_mb = g_malloc(msgsz+sizeof(long)); + host_mb = g_try_malloc(msgsz + sizeof(long)); + if (!host_mb) { + ret = -TARGET_ENOMEM; + goto end; + } ret = get_errno(msgrcv(msqid, host_mb, msgsz, msgtyp, msgflg)); if (ret > 0) { -- cgit v1.1 From 716f3fbef26b583d207f8b7c98cadfa9dda69c6b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Mar 2016 16:25:17 +0000 Subject: linux-user: x86_64: Don't use 16-bit UIDs The 64-bit x86 syscall ABI uses 32-bit UIDs; only define USE_UID16 for 32-bit x86. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall_defs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux-user') diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 9e2b3c2..34af15a 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -55,7 +55,8 @@ #define TARGET_IOC_NRBITS 8 #define TARGET_IOC_TYPEBITS 8 -#if defined(TARGET_I386) || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ + || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \ || defined(TARGET_SPARC) \ || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) /* 16 bit uid wrappers emulation */ -- cgit v1.1 From fd6f7798ac3066ad9e3956defd37521830197666 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Mar 2016 16:33:02 +0000 Subject: linux-user: Use direct syscalls for setuid(), etc On Linux the setuid(), setgid(), etc system calls have different semantics from the libc functions. The libc functions follow POSIX and update the credentials for all threads in the process; the system calls update only the thread which makes the call. (This impedance mismatch is worked around in libc by signalling all threads to tell them to do a syscall, in a byzantine and fragile way; see http://ewontfix.com/17/.) Since in linux-user we are trying to emulate the system call semantics, we must implement all these syscalls to directly call the underlying host syscall, rather than calling the host libc function. Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/syscall.c | 58 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 12 deletions(-) (limited to 'linux-user') diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 40e8742..df70255 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5099,6 +5099,40 @@ static inline int tswapid(int id) #endif /* USE_UID16 */ +/* We must do direct syscalls for setting UID/GID, because we want to + * implement the Linux system call semantics of "change only for this thread", + * not the libc/POSIX semantics of "change for all threads in process". + * (See http://ewontfix.com/17/ for more details.) + * We use the 32-bit version of the syscalls if present; if it is not + * then either the host architecture supports 32-bit UIDs natively with + * the standard syscall, or the 16-bit UID is the best we can do. + */ +#ifdef __NR_setuid32 +#define __NR_sys_setuid __NR_setuid32 +#else +#define __NR_sys_setuid __NR_setuid +#endif +#ifdef __NR_setgid32 +#define __NR_sys_setgid __NR_setgid32 +#else +#define __NR_sys_setgid __NR_setgid +#endif +#ifdef __NR_setresuid32 +#define __NR_sys_setresuid __NR_setresuid32 +#else +#define __NR_sys_setresuid __NR_setresuid +#endif +#ifdef __NR_setresgid32 +#define __NR_sys_setresgid __NR_setresgid32 +#else +#define __NR_sys_setresgid __NR_setresgid +#endif + +_syscall1(int, sys_setuid, uid_t, uid) +_syscall1(int, sys_setgid, gid_t, gid) +_syscall3(int, sys_setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) +_syscall3(int, sys_setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) + void syscall_init(void) { IOCTLEntry *ie; @@ -8834,9 +8868,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setresuid case TARGET_NR_setresuid: - ret = get_errno(setresuid(low2highuid(arg1), - low2highuid(arg2), - low2highuid(arg3))); + ret = get_errno(sys_setresuid(low2highuid(arg1), + low2highuid(arg2), + low2highuid(arg3))); break; #endif #ifdef TARGET_NR_getresuid @@ -8855,9 +8889,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_getresgid case TARGET_NR_setresgid: - ret = get_errno(setresgid(low2highgid(arg1), - low2highgid(arg2), - low2highgid(arg3))); + ret = get_errno(sys_setresgid(low2highgid(arg1), + low2highgid(arg2), + low2highgid(arg3))); break; #endif #ifdef TARGET_NR_getresgid @@ -8883,10 +8917,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #endif case TARGET_NR_setuid: - ret = get_errno(setuid(low2highuid(arg1))); + ret = get_errno(sys_setuid(low2highuid(arg1))); break; case TARGET_NR_setgid: - ret = get_errno(setgid(low2highgid(arg1))); + ret = get_errno(sys_setgid(low2highgid(arg1))); break; case TARGET_NR_setfsuid: ret = get_errno(setfsuid(arg1)); @@ -9168,7 +9202,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setresuid32 case TARGET_NR_setresuid32: - ret = get_errno(setresuid(arg1, arg2, arg3)); + ret = get_errno(sys_setresuid(arg1, arg2, arg3)); break; #endif #ifdef TARGET_NR_getresuid32 @@ -9187,7 +9221,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setresgid32 case TARGET_NR_setresgid32: - ret = get_errno(setresgid(arg1, arg2, arg3)); + ret = get_errno(sys_setresgid(arg1, arg2, arg3)); break; #endif #ifdef TARGET_NR_getresgid32 @@ -9214,12 +9248,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setuid32 case TARGET_NR_setuid32: - ret = get_errno(setuid(arg1)); + ret = get_errno(sys_setuid(arg1)); break; #endif #ifdef TARGET_NR_setgid32 case TARGET_NR_setgid32: - ret = get_errno(setgid(arg1)); + ret = get_errno(sys_setgid(arg1)); break; #endif #ifdef TARGET_NR_setfsuid32 -- cgit v1.1 From 167e4cdc29985e69769452fade698c5b9df78b3d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 3 Mar 2016 12:11:18 +0000 Subject: linux-user: arm: Remove ARM_cpsr and similar #defines The #defines of ARM_cpsr and friends in linux-user/arm/target-syscall.h can clash with versions in the system headers if building on an ARM or AArch64 build (though this seems to be dependent on the version of the system headers). The QEMU defines are not very useful (it's not clear that they're intended for use with the target_pt_regs struct rather than (say) the CPUARMState structure) and we only use them in one function in elfload.c anyway. So just remove the #defines and directly access regs->uregs[]. Reported-by: Christopher Covington Tested-by: Christopher Covington Signed-off-by: Peter Maydell Signed-off-by: Riku Voipio --- linux-user/arm/target_syscall.h | 20 +------------------- linux-user/elfload.c | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 28 deletions(-) (limited to 'linux-user') diff --git a/linux-user/arm/target_syscall.h b/linux-user/arm/target_syscall.h index ea863db..11077b7 100644 --- a/linux-user/arm/target_syscall.h +++ b/linux-user/arm/target_syscall.h @@ -4,29 +4,11 @@ /* this struct defines the way the registers are stored on the stack during a system call. */ +/* uregs[0..15] are r0 to r15; uregs[16] is CPSR; uregs[17] is ORIG_r0 */ struct target_pt_regs { abi_long uregs[18]; }; -#define ARM_cpsr uregs[16] -#define ARM_pc uregs[15] -#define ARM_lr uregs[14] -#define ARM_sp uregs[13] -#define ARM_ip uregs[12] -#define ARM_fp uregs[11] -#define ARM_r10 uregs[10] -#define ARM_r9 uregs[9] -#define ARM_r8 uregs[8] -#define ARM_r7 uregs[7] -#define ARM_r6 uregs[6] -#define ARM_r5 uregs[5] -#define ARM_r4 uregs[4] -#define ARM_r3 uregs[3] -#define ARM_r2 uregs[2] -#define ARM_r1 uregs[1] -#define ARM_r0 uregs[0] -#define ARM_ORIG_r0 uregs[17] - #define ARM_SYSCALL_BASE 0x900000 #define ARM_THUMB_SYSCALL 0 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e47caff..bb2558f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -274,19 +274,20 @@ static inline void init_thread(struct target_pt_regs *regs, abi_long stack = infop->start_stack; memset(regs, 0, sizeof(*regs)); - regs->ARM_cpsr = 0x10; - if (infop->entry & 1) - regs->ARM_cpsr |= CPSR_T; - regs->ARM_pc = infop->entry & 0xfffffffe; - regs->ARM_sp = infop->start_stack; + regs->uregs[16] = ARM_CPU_MODE_USR; + if (infop->entry & 1) { + regs->uregs[16] |= CPSR_T; + } + regs->uregs[15] = infop->entry & 0xfffffffe; + regs->uregs[13] = infop->start_stack; /* FIXME - what to for failure of get_user()? */ - get_user_ual(regs->ARM_r2, stack + 8); /* envp */ - get_user_ual(regs->ARM_r1, stack + 4); /* envp */ + get_user_ual(regs->uregs[2], stack + 8); /* envp */ + get_user_ual(regs->uregs[1], stack + 4); /* envp */ /* XXX: it seems that r0 is zeroed after ! */ - regs->ARM_r0 = 0; + regs->uregs[0] = 0; /* For uClinux PIC binaries. */ /* XXX: Linux does this only on ARM with no MMU (do we care ?) */ - regs->ARM_r10 = infop->start_data; + regs->uregs[10] = infop->start_data; } #define ELF_NREG 18 -- cgit v1.1 From f1d9d1071c01bc5852750b0a829cf45f32cde709 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Tue, 29 Mar 2016 21:53:49 +0800 Subject: linux-user/signal.c: Generate opcode data for restorer in setup_rt_frame Original implementation uses do_rt_sigreturn directly in host space, when a guest program is in unwind procedure in guest space, it will get an incorrect restore address, then causes unwind failure. Also cleanup the original incorrect indentation. Reviewed-by: Laurent Vivier Signed-off-by: Chen Gang Signed-off-by: Riku Voipio --- linux-user/signal.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'linux-user') diff --git a/linux-user/signal.c b/linux-user/signal.c index ff4de4f..a072fa6 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -5599,8 +5599,13 @@ struct target_rt_sigframe { unsigned char save_area[16]; /* caller save area */ struct target_siginfo info; struct target_ucontext uc; + abi_ulong retcode[2]; }; +#define INSN_MOVELI_R10_139 0x00045fe551483000ULL /* { moveli r10, 139 } */ +#define INSN_SWINT1 0x286b180051485000ULL /* { swint1 } */ + + static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env, int signo) { @@ -5676,9 +5681,12 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); setup_sigcontext(&frame->uc.tuc_mcontext, env, info->si_signo); - restorer = (unsigned long) do_rt_sigreturn; if (ka->sa_flags & TARGET_SA_RESTORER) { - restorer = (unsigned long) ka->sa_restorer; + restorer = (unsigned long) ka->sa_restorer; + } else { + __put_user(INSN_MOVELI_R10_139, &frame->retcode[0]); + __put_user(INSN_SWINT1, &frame->retcode[1]); + restorer = frame_addr + offsetof(struct target_rt_sigframe, retcode); } env->pc = (unsigned long) ka->_sa_handler; env->regs[TILEGX_R_SP] = (unsigned long) frame; -- cgit v1.1 From 166c97edd682878943f496f1a3cbed49e096d43b Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Tue, 29 Mar 2016 22:13:45 +0800 Subject: linux-user/signal.c: Use target address instead of host address for microblaze restorer The return address is in target space, so the restorer address needs to be target space, too. Signed-off-by: Chen Gang Reviewed-by: Peter Maydell Reviewed-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/signal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'linux-user') diff --git a/linux-user/signal.c b/linux-user/signal.c index a072fa6..c75fb48 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3573,7 +3573,8 @@ static void setup_frame(int sig, struct target_sigaction *ka, /* Return from sighandler will jump to the tramp. Negative 8 offset because return is rtsd r15, 8 */ - env->regs[15] = ((unsigned long)frame->tramp) - 8; + env->regs[15] = frame_addr + offsetof(struct target_signal_frame, tramp) + - 8; } /* Set up registers for signal handler */ -- cgit v1.1 From 5b1d59d0bb2a30d9fd8e8def88cba2ead7006ece Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Tue, 24 May 2016 14:54:32 +0300 Subject: linux-user/signal.c: Use s390 target space address instead of host space The return address is in target space, so the restorer address needs to be target space, too. Signed-off-by: Chen Gang Reviewed-by: Peter Maydell Reviewed-by: Laurent Vivier --- linux-user/signal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'linux-user') diff --git a/linux-user/signal.c b/linux-user/signal.c index c75fb48..28ce921 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4159,8 +4159,8 @@ static void setup_frame(int sig, struct target_sigaction *ka, env->regs[14] = (unsigned long) ka->sa_restorer | PSW_ADDR_AMODE; } else { - env->regs[14] = (unsigned long) - frame->retcode | PSW_ADDR_AMODE; + env->regs[14] = (frame_addr + offsetof(sigframe, retcode)) + | PSW_ADDR_AMODE; __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, (uint16_t *)(frame->retcode)); } -- cgit v1.1 From 49e55cbacf4ad08f831b9f3f9cb0f3082883a3a1 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 30 Mar 2016 18:36:51 +0200 Subject: linux-user,target-ppc: fix use of MSR_LE setup_frame()/setup_rt_frame()/restore_user_regs() are using MSR_LE as the similar kernel functions do: as a bitmask. But in QEMU, MSR_LE is a bit position, so change this accordingly. The previous code was doing nothing as MSR_LE is 0, and "env->msr &= ~MSR_LE" doesn't change the value of msr. And yes, a user process can change its endianness, see linux kernel commit: fab5db9 [PATCH] powerpc: Implement support for setting little-endian mode via prctl and prctl(2): PR_SET_ENDIAN, PR_GET_ENDIAN Reviewed-by: Thomas Huth Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/signal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'linux-user') diff --git a/linux-user/signal.c b/linux-user/signal.c index 28ce921..8090b4d 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4632,7 +4632,7 @@ static void restore_user_regs(CPUPPCState *env, /* If doing signal return, restore the previous little-endian mode. */ if (sig) - env->msr = (env->msr & ~MSR_LE) | (msr & MSR_LE); + env->msr = (env->msr & ~(1ull << MSR_LE)) | (msr & (1ull << MSR_LE)); /* Restore Altivec registers if necessary. */ if (env->insns_flags & PPC_ALTIVEC) { @@ -4747,7 +4747,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, #endif /* Signal handlers are entered in big-endian mode. */ - env->msr &= ~MSR_LE; + env->msr &= ~(1ull << MSR_LE); unlock_user_struct(frame, frame_addr, 1); return; @@ -4842,7 +4842,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, #endif /* Signal handlers are entered in big-endian mode. */ - env->msr &= ~MSR_LE; + env->msr &= ~(1ull << MSR_LE); unlock_user_struct(rt_sf, rt_sf_addr, 1); return; -- cgit v1.1