Skip to content

Commit 3f17fed

Browse files
benzeajmberg-intel
authored andcommitted
um: switch to regset API and depend on XSTATE
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. Signed-off-by: Benjamin Berg <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Johannes Berg <[email protected]>
1 parent 0b8b266 commit 3f17fed

File tree

18 files changed

+471
-473
lines changed

18 files changed

+471
-473
lines changed

arch/um/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ menu "UML-specific options"
55
config UML
66
bool
77
default y
8+
select ARCH_WANTS_DYNAMIC_TASK_STRUCT
89
select ARCH_HAS_CPU_FINALIZE_INIT
910
select ARCH_HAS_FORTIFY_SOURCE
1011
select ARCH_HAS_GCOV_PROFILE_ALL
@@ -32,6 +33,7 @@ config UML
3233
select HAVE_ARCH_VMAP_STACK
3334
select HAVE_RUST
3435
select ARCH_HAS_UBSAN
36+
select HAVE_ARCH_TRACEHOOK
3537

3638
config MMU
3739
bool

arch/um/include/asm/processor-generic.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ struct task_struct;
2020
struct mm_struct;
2121

2222
struct thread_struct {
23-
struct pt_regs regs;
2423
struct pt_regs *segv_regs;
2524
struct task_struct *prev_sched;
2625
struct arch_thread arch;
@@ -31,6 +30,9 @@ struct thread_struct {
3130
void *arg;
3231
} thread;
3332
} request;
33+
34+
/* Contains variable sized FP registers */
35+
struct pt_regs regs;
3436
};
3537

3638
#define INIT_THREAD \

arch/um/kernel/process.c

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ void initial_thread_cb(void (*proc)(void *), void *arg)
187187
kmalloc_ok = save_kmalloc_ok;
188188
}
189189

190+
int arch_dup_task_struct(struct task_struct *dst,
191+
struct task_struct *src)
192+
{
193+
memcpy(dst, src, arch_task_struct_size);
194+
return 0;
195+
}
196+
190197
void um_idle_sleep(void)
191198
{
192199
if (time_travel_mode != TT_MODE_OFF)
@@ -287,18 +294,3 @@ unsigned long __get_wchan(struct task_struct *p)
287294

288295
return 0;
289296
}
290-
291-
int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu)
292-
{
293-
#ifdef CONFIG_X86_32
294-
extern int have_fpx_regs;
295-
296-
/* FIXME: A plain copy does not work on i386 with have_fpx_regs */
297-
if (have_fpx_regs)
298-
return 0;
299-
#endif
300-
memcpy(fpu, &t->thread.regs.regs.fp, sizeof(*fpu));
301-
302-
return 1;
303-
}
304-

arch/um/kernel/um_arch.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ int __init linux_main(int argc, char **argv, char **envp)
402402
os_info("Kernel virtual memory size shrunk to %lu bytes\n",
403403
virtmem_size);
404404

405+
arch_task_struct_size = sizeof(struct task_struct) + host_fp_size;
406+
405407
os_flush_stdout();
406408

407409
return start_uml();

arch/um/os-Linux/registers.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
#include <sysdep/ptrace.h>
1111
#include <sysdep/ptrace_user.h>
1212
#include <registers.h>
13+
#include <stdlib.h>
1314

1415
/* This is set once at boot time and not changed thereafter */
1516

1617
static unsigned long exec_regs[MAX_REG_NR];
17-
static unsigned long exec_fp_regs[FP_SIZE];
18+
static unsigned long *exec_fp_regs;
1819

1920
int init_pid_registers(int pid)
2021
{
@@ -24,7 +25,11 @@ int init_pid_registers(int pid)
2425
if (err < 0)
2526
return -errno;
2627

27-
arch_init_registers(pid);
28+
err = arch_init_registers(pid);
29+
if (err < 0)
30+
return err;
31+
32+
exec_fp_regs = malloc(host_fp_size);
2833
get_fp_registers(pid, exec_fp_regs);
2934
return 0;
3035
}
@@ -34,5 +39,5 @@ void get_safe_registers(unsigned long *regs, unsigned long *fp_regs)
3439
memcpy(regs, exec_regs, sizeof(exec_regs));
3540

3641
if (fp_regs)
37-
memcpy(fp_regs, exec_fp_regs, sizeof(exec_fp_regs));
42+
memcpy(fp_regs, exec_fp_regs, host_fp_size);
3843
}

arch/x86/um/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ else
1010
endif
1111

1212
obj-y = bugs_$(BITS).o delay.o fault.o \
13-
ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
13+
ptrace.o ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
1414
stub_segv.o \
1515
sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \
1616
mem_$(BITS).o subarch.o os-Linux/

arch/x86/um/asm/elf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <asm/user.h>
99
#include <skas.h>
1010

11+
#define CORE_DUMP_USE_REGSET
12+
1113
#ifdef CONFIG_X86_32
1214

1315
#define R_386_NONE 0

arch/x86/um/asm/ptrace.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22
#ifndef __UM_X86_PTRACE_H
33
#define __UM_X86_PTRACE_H
44

5+
/* This is here because signal.c needs the REGSET_FP_LEGACY definition */
6+
enum {
7+
REGSET_GENERAL,
8+
#ifdef CONFIG_X86_32
9+
REGSET_FP_LEGACY,
10+
#endif
11+
REGSET_FP,
12+
REGSET_XSTATE,
13+
};
14+
515
#include <linux/compiler.h>
616
#ifndef CONFIG_X86_32
717
#define __FRAME_OFFSETS /* Needed to get the R* macros */

arch/x86/um/os-Linux/registers.c

Lines changed: 35 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -16,133 +16,58 @@
1616
#include <asm/sigcontext.h>
1717
#include <linux/elf.h>
1818
#include <registers.h>
19+
#include <sys/mman.h>
1920

20-
static int have_xstate_support;
21+
unsigned long host_fp_size;
2122

22-
static int save_i387_registers(int pid, unsigned long *fp_regs)
23-
{
24-
if (ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0)
25-
return -errno;
26-
return 0;
27-
}
28-
29-
static int save_fp_registers(int pid, unsigned long *fp_regs)
23+
int get_fp_registers(int pid, unsigned long *regs)
3024
{
31-
#ifdef PTRACE_GETREGSET
32-
struct iovec iov;
25+
struct iovec iov = {
26+
.iov_base = regs,
27+
.iov_len = host_fp_size,
28+
};
3329

34-
if (have_xstate_support) {
35-
iov.iov_base = fp_regs;
36-
iov.iov_len = FP_SIZE * sizeof(unsigned long);
37-
if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) < 0)
38-
return -errno;
39-
return 0;
40-
} else
41-
#endif
42-
return save_i387_registers(pid, fp_regs);
43-
}
44-
45-
static int restore_i387_registers(int pid, unsigned long *fp_regs)
46-
{
47-
if (ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0)
30+
if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) < 0)
4831
return -errno;
4932
return 0;
5033
}
5134

52-
static int restore_fp_registers(int pid, unsigned long *fp_regs)
53-
{
54-
#ifdef PTRACE_SETREGSET
55-
struct iovec iov;
56-
if (have_xstate_support) {
57-
iov.iov_base = fp_regs;
58-
iov.iov_len = FP_SIZE * sizeof(unsigned long);
59-
if (ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov) < 0)
60-
return -errno;
61-
return 0;
62-
} else
63-
#endif
64-
return restore_i387_registers(pid, fp_regs);
65-
}
66-
67-
#ifdef __i386__
68-
int have_fpx_regs = 1;
69-
static int save_fpx_registers(int pid, unsigned long *fp_regs)
35+
int put_fp_registers(int pid, unsigned long *regs)
7036
{
71-
if (ptrace(PTRACE_GETFPXREGS, pid, 0, fp_regs) < 0)
72-
return -errno;
73-
return 0;
74-
}
37+
struct iovec iov = {
38+
.iov_base = regs,
39+
.iov_len = host_fp_size,
40+
};
7541

76-
static int restore_fpx_registers(int pid, unsigned long *fp_regs)
77-
{
78-
if (ptrace(PTRACE_SETFPXREGS, pid, 0, fp_regs) < 0)
42+
if (ptrace(PTRACE_SETREGSET, pid, NT_X86_XSTATE, &iov) < 0)
7943
return -errno;
8044
return 0;
8145
}
8246

83-
int get_fp_registers(int pid, unsigned long *regs)
84-
{
85-
if (have_fpx_regs)
86-
return save_fpx_registers(pid, regs);
87-
else
88-
return save_fp_registers(pid, regs);
89-
}
90-
91-
int put_fp_registers(int pid, unsigned long *regs)
92-
{
93-
if (have_fpx_regs)
94-
return restore_fpx_registers(pid, regs);
95-
else
96-
return restore_fp_registers(pid, regs);
97-
}
98-
99-
void arch_init_registers(int pid)
100-
{
101-
struct user_fpxregs_struct fpx_regs;
102-
int err;
103-
104-
err = ptrace(PTRACE_GETFPXREGS, pid, 0, &fpx_regs);
105-
if (!err)
106-
return;
107-
108-
if (errno != EIO)
109-
panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d",
110-
errno);
111-
112-
have_fpx_regs = 0;
113-
}
114-
#else
115-
116-
int get_fp_registers(int pid, unsigned long *regs)
47+
int arch_init_registers(int pid)
11748
{
118-
return save_fp_registers(pid, regs);
49+
struct iovec iov = {
50+
/* Just use plenty of space, it does not cost us anything */
51+
.iov_len = 2 * 1024 * 1024,
52+
};
53+
int ret;
54+
55+
iov.iov_base = mmap(NULL, iov.iov_len, PROT_WRITE | PROT_READ,
56+
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
57+
if (iov.iov_base == MAP_FAILED)
58+
return -ENOMEM;
59+
60+
/* GDB has x86_xsave_length, which uses x86_cpuid_count */
61+
ret = ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov);
62+
if (ret)
63+
ret = -errno;
64+
munmap(iov.iov_base, 2 * 1024 * 1024);
65+
66+
host_fp_size = iov.iov_len;
67+
68+
return ret;
11969
}
12070

121-
int put_fp_registers(int pid, unsigned long *regs)
122-
{
123-
return restore_fp_registers(pid, regs);
124-
}
125-
126-
void arch_init_registers(int pid)
127-
{
128-
#ifdef PTRACE_GETREGSET
129-
void * fp_regs;
130-
struct iovec iov;
131-
132-
fp_regs = malloc(FP_SIZE * sizeof(unsigned long));
133-
if(fp_regs == NULL)
134-
return;
135-
136-
iov.iov_base = fp_regs;
137-
iov.iov_len = FP_SIZE * sizeof(unsigned long);
138-
if (ptrace(PTRACE_GETREGSET, pid, NT_X86_XSTATE, &iov) == 0)
139-
have_xstate_support = 1;
140-
141-
free(fp_regs);
142-
#endif
143-
}
144-
#endif
145-
14671
unsigned long get_thread_reg(int reg, jmp_buf *buf)
14772
{
14873
switch (reg) {

0 commit comments

Comments
 (0)