Skip to contentSkip to conversation

um: switch to regset API and depend on XSTATE

PATCH Superseded Benjamin Berg 1 participant 1 message Oct 7, 2024

Subsystems (4)

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 um: switch to regset API and depend on XSTATE

From: Benjamin Berg <[email protected]>

The PTRACE_GETREGSET API has now existed since Linux 2.6.33. The XSAVE CPU feature should also be sufficiently common to be able to rely on it.

With this, define our internal FP state to be the hosts XSAVE data. Add discovery for the hosts XSAVE size and place the FP registers at the end of task_struct so that we can adjust the size at runtime.

Next we can implement the regset API on top and update the signal handling as well as ptrace APIs to use them. Also switch coredump creation to use the regset API and finally set HAVE_ARCH_TRACEHOOK.

This considerably improves the signal frames. Previously they might not have contained all the registers (i386) and also did not have the sizes and magic values set to the correct values to permit userspace to decode the frame.

As a side effect, this will permit UML to run on hosts with newer CPU extensions (such as AMX) that need even more register state.

---

v1:
* Use a fixed mmap'ed buffer for size discovery
* Add forgotten enum definition for signal.c
* Fix FPX register size using sizeof()
* Also set magic1 and xstate_size to the expected values

---
 arch/um/Kconfig                         |   2 +
 arch/um/include/asm/processor-generic.h |   4 +-
 arch/um/kernel/process.c                |  15 --
 arch/um/kernel/um_arch.c                |   2 +
 arch/um/os-Linux/registers.c            |   9 +-
 arch/x86/um/Makefile                    |   2 +-
 arch/x86/um/asm/elf.h                   |   2 +
 arch/x86/um/asm/ptrace.h                |  10 +
 arch/x86/um/os-Linux/registers.c        | 145 +++--------
 arch/x86/um/ptrace.c                    | 267 ++++++++++++++++++++
 arch/x86/um/ptrace_32.c                 |  88 ++-----
 arch/x86/um/ptrace_64.c                 |  37 +--
 arch/x86/um/shared/sysdep/ptrace.h      |   8 +-
 arch/x86/um/shared/sysdep/ptrace_32.h   |   4 -
 arch/x86/um/shared/sysdep/ptrace_64.h   |   4 -
 arch/x86/um/shared/sysdep/ptrace_user.h |   6 -
 arch/x86/um/signal.c                    | 322 ++++++++----------------
 arch/x86/um/user-offsets.c              |   9 +-
 18 files changed, 464 insertions(+), 472 deletions(-)
 create mode 100644 arch/x86/um/ptrace.c
arch/um/Kconfig +2 -0
index 48db1c99bd46..88c695db1ecf 100644
@@ -5,6 +5,7 @@ menu "UML-specific options"
 config UML
 	bool
 	default y
+	select ARCH_WANTS_DYNAMIC_TASK_STRUCT
 	select ARCH_HAS_CPU_FINALIZE_INIT
 	select ARCH_HAS_FORTIFY_SOURCE
 	select ARCH_HAS_GCOV_PROFILE_ALL
@@ -32,6 +33,7 @@ config UML
 	select HAVE_ARCH_VMAP_STACK
 	select HAVE_RUST
 	select ARCH_HAS_UBSAN
+	select HAVE_ARCH_TRACEHOOK
 
 config MMU
 	bool
arch/um/include/asm/processor-generic.h +3 -1
index bce4595798da..7571d6dcfa14 100644
@@ -20,7 +20,6 @@ struct task_struct;
 struct mm_struct;
 
 struct thread_struct {
-	struct pt_regs regs;
 	struct pt_regs *segv_regs;
 	void *fault_addr;
 	jmp_buf *fault_catcher;
@@ -33,6 +32,9 @@ struct thread_struct {
 			void *arg;
 		} thread;
 	} request;
+
+	/* Contains variable sized FP registers */
+	struct pt_regs regs;
 };
 
 #define INIT_THREAD \
arch/um/kernel/process.c +0 -15
index d45c79f82d7c..f6ed31223ba9 100644
@@ -287,18 +287,3 @@ unsigned long __get_wchan(struct task_struct *p)
 
 	return 0;
 }
-
-int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu)
-{
-#ifdef CONFIG_X86_32
-	extern int have_fpx_regs;
-
-	/* FIXME: A plain copy does not work on i386 with have_fpx_regs */
-	if (have_fpx_regs)
-		return 0;
-#endif
-	memcpy(fpu, &t->thread.regs.regs.fp, sizeof(*fpu));
-
-	return 1;
-}
-
arch/um/kernel/um_arch.c +2 -0
index 285aa6fed188..91c22e7c0564 100644
@@ -418,6 +418,8 @@ int __init linux_main(int argc, char **argv, char **envp)
 		os_info("Kernel virtual memory size shrunk to %lu bytes\n",
 			virtmem_size);
 
+	arch_task_struct_size = sizeof(struct task_struct) + host_fp_size;
+
 	os_flush_stdout();
 
 	return start_uml();
arch/um/os-Linux/registers.c +7 -2
index bd80b921add0..eb052fc394ee 100644
@@ -10,11 +10,12 @@
 #include <sysdep/ptrace.h>
 #include <sysdep/ptrace_user.h>
 #include <registers.h>
+#include <stdlib.h>
 
 /* This is set once at boot time and not changed thereafter */
 
 static unsigned long exec_regs[MAX_REG_NR];
-static unsigned long exec_fp_regs[FP_SIZE];
+static unsigned long *exec_fp_regs;
 
 int init_pid_registers(int pid)
 {
@@ -24,7 +25,11 @@ int init_pid_registers(int pid)
 	if (err < 0)
 		return -errno;
 
-	arch_init_registers(pid);
+	err = arch_init_registers(pid);
+	if (err < 0)
+		return err;
+
+	exec_fp_regs = malloc(host_fp_size);
 	get_fp_registers(pid, exec_fp_regs);
 	return 0;
 }
arch/x86/um/Makefile +1 -1
index 36e67fc97c22..b42c31cd2390 100644
@@ -10,7 +10,7 @@ else
 endif
 
 obj-y = bugs_$(BITS).o delay.o fault.o \
-	ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
+	ptrace.o ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
 	stub_segv.o \
 	sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \
 	mem_$(BITS).o subarch.o os-Linux/
arch/x86/um/asm/elf.h +2 -0
index 6052200fe925..62ed5d68a978 100644
@@ -8,6 +8,8 @@
 #include <asm/user.h>
 #include <skas.h>
 
+#define CORE_DUMP_USE_REGSET
+
 #ifdef CONFIG_X86_32
 
 #define R_386_NONE	0
arch/x86/um/asm/ptrace.h +10 -0
index 2fef3da55533..2641d28d115c 100644
@@ -2,6 +2,16 @@
 #ifndef __UM_X86_PTRACE_H
 #define __UM_X86_PTRACE_H
 
+/* This is here because signal.c needs the REGSET_FP_LEGACY definition */
+enum {
+	REGSET_GENERAL,
+#ifdef CONFIG_X86_32
+	REGSET_FP_LEGACY,
+#endif
+	REGSET_FP,
+	REGSET_XSTATE,
+};
+
 #include <linux/compiler.h>
 #ifndef CONFIG_X86_32
 #define __FRAME_OFFSETS /* Needed to get the R* macros */
arch/x86/um/os-Linux/registers.c +35 -110

168 lines not on this page · raw message

arch/x86/um/ptrace.c new file +267 -0

267 lines not on this page · raw message

arch/x86/um/ptrace_32.c +17 -71

109 lines not on this page · raw message

arch/x86/um/ptrace_64.c +9 -28
index f8bbad29cd0b..e0d4120a45c8 100644
@@ -8,6 +8,7 @@
 #include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <linux/regset.h>
 #define __FRAME_OFFSETS
 #include <asm/ptrace.h>
 #include <linux/uaccess.h>
@@ -188,30 +189,6 @@ int peek_user(struct task_struct *child, long addr, long data)
 	return put_user(tmp, (unsigned long *) data);
 }
 
-static int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
-{
-	int n;
-
-	n = copy_to_user(buf, &child->thread.regs.regs.fp,
-			 sizeof(struct user_i387_struct));
-	if (n > 0)
-		return -EFAULT;
-
-	return n;
-}
-
-static int set_fpregs(struct user_i387_struct __user *buf, struct task_struct *child)
-{
-	int n;
-
-	n = copy_from_user(&child->thread.regs.regs.fp, buf,
-			   sizeof(struct user_i387_struct));
-	if (n > 0)
-		return -EFAULT;
-
-	return 0;
-}
-
 long subarch_ptrace(struct task_struct *child, long request,
 		    unsigned long addr, unsigned long data)
 {
@@ -220,11 +197,15 @@ long subarch_ptrace(struct task_struct *child, long request,
 
 	switch (request) {
 	case PTRACE_GETFPREGS: /* Get the child FPU state. */
-		ret = get_fpregs(datap, child);
-		break;
+		return copy_regset_to_user(child, task_user_regset_view(child),
+					   REGSET_FP,
+					   0, sizeof(struct user_i387_struct),
+					   datap);
 	case PTRACE_SETFPREGS: /* Set the child FPU state. */
-		ret = set_fpregs(datap, child);
-		break;
+		return copy_regset_from_user(child, task_user_regset_view(child),
+					     REGSET_FP,
+					     0, sizeof(struct user_i387_struct),
+					     datap);
 	case PTRACE_ARCH_PRCTL:
 		/* XXX Calls ptrace on the host - needs some SMP thinking */
 		ret = arch_prctl(child, data, (void __user *) addr);
arch/x86/um/shared/sysdep/ptrace.h +7 -1
index 6ca4ecabc55b..2dd4ca6713f8 100644
@@ -56,12 +56,16 @@ struct syscall_args {
 				     UPT_SYSCALL_ARG5(r),	 \
 				     UPT_SYSCALL_ARG6(r) } } )
 
+extern unsigned long host_fp_size;
+
 struct uml_pt_regs {
 	unsigned long gp[MAX_REG_NR];
-	unsigned long fp[MAX_FP_NR];
 	struct faultinfo faultinfo;
 	long syscall;
 	int is_user;
+
+	/* Dynamically sized FP registers (holds an XSTATE) */
+	unsigned long fp[];
 };
 
 #define EMPTY_UML_PT_REGS { }
@@ -72,4 +76,6 @@ struct uml_pt_regs {
 
 extern int user_context(unsigned long sp);
 
+extern int arch_init_registers(int pid);
+
 #endif /* __SYSDEP_X86_PTRACE_H */
arch/x86/um/shared/sysdep/ptrace_32.h +0 -4
index 0c4989842fbe..2392470cac4d 100644
@@ -6,8 +6,6 @@
 #ifndef __SYSDEP_I386_PTRACE_H
 #define __SYSDEP_I386_PTRACE_H
 
-#define MAX_FP_NR HOST_FPX_SIZE
-
 #define UPT_SYSCALL_ARG1(r) UPT_BX(r)
 #define UPT_SYSCALL_ARG2(r) UPT_CX(r)
 #define UPT_SYSCALL_ARG3(r) UPT_DX(r)
@@ -15,6 +13,4 @@
 #define UPT_SYSCALL_ARG5(r) UPT_DI(r)
 #define UPT_SYSCALL_ARG6(r) UPT_BP(r)
 
-extern void arch_init_registers(int pid);
-
 #endif
arch/x86/um/shared/sysdep/ptrace_64.h +0 -4
index 0dc223aa1c2d..e73573ac871f 100644
@@ -8,8 +8,6 @@
 #ifndef __SYSDEP_X86_64_PTRACE_H
 #define __SYSDEP_X86_64_PTRACE_H
 
-#define MAX_FP_NR HOST_FP_SIZE
-
 #define REGS_R8(r) ((r)[HOST_R8])
 #define REGS_R9(r) ((r)[HOST_R9])
 #define REGS_R10(r) ((r)[HOST_R10])
@@ -57,6 +55,4 @@
 #define UPT_SYSCALL_ARG5(r) UPT_R8(r)
 #define UPT_SYSCALL_ARG6(r) UPT_R9(r)
 
-extern void arch_init_registers(int pid);
-
 #endif
arch/x86/um/shared/sysdep/ptrace_user.h +0 -6
index 1d1a824fa652..98da23120538 100644
@@ -11,12 +11,6 @@
 #define REGS_IP_INDEX HOST_IP
 #define REGS_SP_INDEX HOST_SP
 
-#ifdef __i386__
-#define FP_SIZE ((HOST_FPX_SIZE > HOST_FP_SIZE) ? HOST_FPX_SIZE : HOST_FP_SIZE)
-#else
-#define FP_SIZE HOST_FP_SIZE
-#endif
-
 /*
  * glibc before 2.27 does not include PTRACE_SYSEMU_SINGLESTEP in its enum,
  * ensure we have a definition by (re-)defining it here.
arch/x86/um/signal.c +101 -221

429 lines not on this page · raw message

arch/x86/um/user-offsets.c +1 -8
index 1c77d9946199..ffa653257c6b 100644
@@ -19,10 +19,8 @@ void foo(void);
 
 void foo(void)
 {
-#ifdef __i386__
-	DEFINE_LONGS(HOST_FP_SIZE, sizeof(struct user_fpregs_struct));
-	DEFINE_LONGS(HOST_FPX_SIZE, sizeof(struct user_fpxregs_struct));
 
+#ifdef __i386__
 	DEFINE(HOST_IP, EIP);
 	DEFINE(HOST_SP, UESP);
 	DEFINE(HOST_EFLAGS, EFL);
@@ -41,11 +39,6 @@ void foo(void)
 	DEFINE(HOST_GS, GS);
 	DEFINE(HOST_ORIG_AX, ORIG_EAX);
 #else
-#ifdef FP_XSTATE_MAGIC1
-	DEFINE_LONGS(HOST_FP_SIZE, 2696);
-#else
-	DEFINE(HOST_FP_SIZE, sizeof(struct _fpstate) / sizeof(unsigned long));
-#endif
 	DEFINE_LONGS(HOST_BX, RBX);
 	DEFINE_LONGS(HOST_CX, RCX);
 	DEFINE_LONGS(HOST_DI, RDI);
Signature · 1 line
2.46.2