From: James Hilliard <[email protected]> Cavium Octeon userspace is not following a generic MIPS Linux TLS ABI rule here. Older Octeon glibc uses the k0 register as the fast thread pointer, while newer Octeon2 and Octeon3 glibc variants use the normal rdhwr $29 path.
linux-user already updates CP0_UserLocal for cpu_set_tls() and TARGET_NR_set_thread_area, but it does not keep gpr[26] synchronized. That leaves EF_MIPS_MACH_OCTEON userlands able to complete set_thread_area() and still reach pthread startup or pthread_self() with a stale k0 value. Use the existing MIPS ELF machine flags from linux-user/elfload.c and mirror CP0_UserLocal into gpr[26] only for EF_MIPS_MACH_OCTEON. Signed-off-by: James Hilliard <[email protected]> Reviewed-by: Richard Henderson <[email protected]> Signed-off-by: Helge Deller <[email protected]> (cherry picked from commit 4c681ba3b82d9a9f00a3f361399a1bb7612f3535) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0f05db4715..a791396ead 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3405,6 +3405,9 @@ static void load_elf_image(const char *image_name, const ImageSource *src, /* Usual start for brk is after all sections of the main executable. */ info->brk = TARGET_PAGE_ALIGN(hiaddr + load_bias); info->elf_flags = ehdr->e_flags; +#ifdef TARGET_MIPS + info->use_k0_tls = (ehdr->e_flags & EF_MIPS_MACH) == EF_MIPS_MACH_OCTEON; +#endif prot_exec = PROT_EXEC; #ifdef TARGET_AARCH64 diff --git a/linux-user/mips/target_cpu.h b/linux-user/mips/target_cpu.h index c375616c55..2bbd0a81c5 100644 --- a/linux-user/mips/target_cpu.h +++ b/linux-user/mips/target_cpu.h @@ -35,7 +35,12 @@ static inline void cpu_clone_regs_parent(CPUMIPSState *env, unsigned flags) static inline void cpu_set_tls(CPUMIPSState *env, target_ulong newtls) { + TaskState *ts = get_task_state(env_cpu(env)); + env->active_tc.CP0_UserLocal = newtls; + if (ts->info->use_k0_tls) { + env->active_tc.gpr[26] = newtls; + } } static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 5f00750151..f2b16ef54b 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -64,6 +64,7 @@ struct image_info { uint32_t note_flags; #ifdef TARGET_MIPS + bool use_k0_tls; int fp_abi; int interp_fp_abi; #endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index f2c8037356..ea1711ff95 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -12972,7 +12972,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_set_thread_area case TARGET_NR_set_thread_area: #if defined(TARGET_MIPS) - cpu_env->active_tc.CP0_UserLocal = arg1; + cpu_set_tls(cpu_env, arg1); return 0; #elif defined(TARGET_I386) && defined(TARGET_ABI32) return do_set_thread_area(cpu_env, arg1); -- 2.47.3
