From 4c681ba3b82d9a9f00a3f361399a1bb7612f3535 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Thu, 9 Apr 2026 20:00:12 -0600 Subject: [PATCH] linux-user/mips: sync k0 TLS for EF_MIPS_MACH_OCTEON userlands 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 Reviewed-by: Richard Henderson Signed-off-by: Helge Deller --- linux-user/elfload.c | 3 +++ linux-user/mips/target_cpu.h | 5 +++++ linux-user/qemu.h | 1 + linux-user/syscall.c | 2 +- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 59b543f740..0e757787d2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1476,6 +1476,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 cfe5f45fc4..7f98fb2607 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -65,6 +65,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 f4b74ad350..8e96cc26db 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -13216,7 +13216,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);