From deef986e6c665c0bdd5fa41cac39567525a53523 Mon Sep 17 00:00:00 2001 From: anton Date: Wed, 27 Nov 2024 05:25:56 +0000 Subject: [PATCH] Add ptrace commands used to read/write the XSAVE area of a traced process. Intended to give debuggers access to xmm/ymm registers. Inspired by FreeBSD which exposes a similar set of ptrace commands. ok kettenis@ --- lib/libc/sys/ptrace.2 | 62 ++++++++++++++++++- sys/arch/amd64/amd64/process_machdep.c | 83 +++++++++++++++++++++++++- sys/arch/amd64/include/ptrace.h | 17 +++++- sys/kern/sys_process.c | 33 +++++++++- 4 files changed, 190 insertions(+), 5 deletions(-) diff --git a/lib/libc/sys/ptrace.2 b/lib/libc/sys/ptrace.2 index 06dd079a980..9b4b083ffb1 100644 --- a/lib/libc/sys/ptrace.2 +++ b/lib/libc/sys/ptrace.2 @@ -1,8 +1,8 @@ -.\" $OpenBSD: ptrace.2,v 1.43 2022/09/11 06:38:11 jmc Exp $ +.\" $OpenBSD: ptrace.2,v 1.44 2024/11/27 05:25:56 anton Exp $ .\" $NetBSD: ptrace.2,v 1.3 1996/02/23 01:39:41 jtc Exp $ .\" .\" This file is in the public domain. -.Dd $Mdocdate: September 11 2022 $ +.Dd $Mdocdate: November 27 2024 $ .Dt PTRACE 2 .Os .Sh NAME @@ -500,6 +500,58 @@ pointed to by The window cookie needs to be .Sq XOR'ed to stack-saved program counters. +.It Dv PT_GETXSTATE_INFO Pq amd64 only +This request can be used to obtain details about the traced process XSAVE area, +specified in a +.Dq Li "struct ptrace_xstate_info" +defined as follows: +.Bd -literal -offset indent +struct ptrace_xstate_info { + uint64_t xsave_mask; + uint32_t xsave_len; +}; +.Ed +.Pp +The +.Fa xsave_mask +field denotes the enabled XSAVE components. +The +.Fa xsave_len +field denotes the size of XSAVE area intended to be used with the +.Dv PT_GETXSTATE +and +.Dv PT_SETXSTATE +requests. +.Pp +A pointer to +.Dq Li "struct ptrace_xstate_info" +must be passed in +.Fa addr +and the +.Fa data +argument must be set to +.Li sizeof(struct ptrace_xstate_info) . +.It Dv PT_GETXSTATE Pq amd64 only +This request can be used to read the XSAVE area of the traced process. +A pointer to a buffer must be passed in +.Fa addr +with a capacity of the length obtained using the +.Dv PT_GETXSTATE_INFO +request. +The +.Fa data +argument must reflect the same length. +.It Dv PT_SETXSTATE Pq amd64 only +This request can be used to write the XSAVE area of the traced process. +Only changes to the x87, SSE and AVX state components are honored. +A pointer to a buffer must be passed in +.Fa addr +with a capacity of the length obtained using the +.Dv PT_GETXSTATE_INFO +request. +The +.Fa data +argument must reflect the same length. .El .Sh ERRORS Some requests can cause @@ -579,6 +631,12 @@ An attempt was made to use .Dv PT_ATTACH on a system process. .El +.It Bq Er ENOTSUP +.Dv PT_GETXSTATE_INFO , +.Dv PT_GETXSTATE , +or +.Dv PT_SETXSTATE +was attempted on a CPU lacking support for XSAVE. .El .Sh HISTORY The diff --git a/sys/arch/amd64/amd64/process_machdep.c b/sys/arch/amd64/amd64/process_machdep.c index f6c27e4eb7e..ab98c40b558 100644 --- a/sys/arch/amd64/amd64/process_machdep.c +++ b/sys/arch/amd64/amd64/process_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: process_machdep.c,v 1.17 2024/05/13 01:15:50 jsg Exp $ */ +/* $OpenBSD: process_machdep.c,v 1.18 2024/11/27 05:25:56 anton Exp $ */ /* $NetBSD: process_machdep.c,v 1.1 2003/04/26 18:39:31 fvdl Exp $ */ /*- @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -200,4 +201,84 @@ process_set_pc(struct proc *p, caddr_t addr) return (0); } +int +process_read_xstate_info(struct proc *p, void *addr) +{ + struct ptrace_xstate_info *info = addr; + + if (xsave_mask == 0) + return (ENOTSUP); + + info->xsave_mask = xsave_mask; + info->xsave_len = fpu_save_len; + return (0); +} + +struct xsave_area { + uint8_t legacy_region[512]; + + struct xsave_header { + uint64_t xstate_bv; + uint64_t xcomp_bv; + uint8_t rsvd0[48]; + } xsave_header; + + uint8_t extended_region[]; +} __attribute__((packed)); + +#define XSTATE_COMPONENT_X87 (1ULL << 0) +#define XSTATE_COMPONENT_SSE (1ULL << 1) +#define XSTATE_COMPONENT_AVX (1ULL << 2) + +int +process_read_xstate(struct proc *p, void *addr) +{ + struct xsave_area *area = + (struct xsave_area *)&p->p_addr->u_pcb.pcb_savefpu; + + if (xsave_mask == 0) + return (ENOTSUP); + + memcpy(addr, area, fpu_save_len); + return (0); +} + +int +process_write_xstate(struct proc *p, void *addr) +{ + struct xsave_area *area = + (struct xsave_area *)&p->p_addr->u_pcb.pcb_savefpu; + struct xsave_area *new_area = (struct xsave_area *)addr; + uint32_t offset_extended_region = offsetof(struct xsave_area, + extended_region); + uint32_t a, b, c, d; + + if (xsave_mask == 0) + return (ENOTSUP); + + /* + * Honor changes to x87, SSE and AVX components and mark them as in use. + * Required to ensure any changes are restored once the traced process + * continues execution. + */ + if ((xsave_mask & XSTATE_COMPONENT_X87) || + (xsave_mask & XSTATE_COMPONENT_SSE)) { + memcpy(area->legacy_region, new_area->legacy_region, + sizeof(area->legacy_region)); + area->xsave_header.xstate_bv |= xsave_mask & + (XSTATE_COMPONENT_X87 | XSTATE_COMPONENT_SSE); + } + if (xsave_mask & XSTATE_COMPONENT_AVX) { + CPUID_LEAF(0xd, 2, a, b, c, d); + if (offset_extended_region == b && + offset_extended_region + a <= fpu_save_len) { + memcpy(area->extended_region, + new_area->extended_region, a); + area->xsave_header.xstate_bv |= XSTATE_COMPONENT_AVX; + } + } + + return (0); +} + #endif /* PTRACE */ diff --git a/sys/arch/amd64/include/ptrace.h b/sys/arch/amd64/include/ptrace.h index 27c0bf29c1d..7ab2acea521 100644 --- a/sys/arch/amd64/include/ptrace.h +++ b/sys/arch/amd64/include/ptrace.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ptrace.h,v 1.1 2004/01/28 01:39:39 mickey Exp $ */ +/* $OpenBSD: ptrace.h,v 1.2 2024/11/27 05:25:57 anton Exp $ */ /* $NetBSD: ptrace.h,v 1.1 2003/04/26 18:39:47 fvdl Exp $ */ /* @@ -39,3 +39,18 @@ #define PT_SETREGS (PT_FIRSTMACH + 2) #define PT_GETFPREGS (PT_FIRSTMACH + 3) #define PT_SETFPREGS (PT_FIRSTMACH + 4) + +#define PT_GETXSTATE_INFO (PT_FIRSTMACH + 5) +#define PT_GETXSTATE (PT_FIRSTMACH + 6) +#define PT_SETXSTATE (PT_FIRSTMACH + 7) + +struct ptrace_xstate_info { + uint64_t xsave_mask; + uint32_t xsave_len; +}; + +#ifdef _KERNEL +int process_read_xstate_info(struct proc *, void *); +int process_read_xstate(struct proc *, void *); +int process_write_xstate(struct proc *, void *); +#endif diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 6ab263c7eca..ecf487b724b 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sys_process.c,v 1.102 2024/10/08 12:02:24 claudio Exp $ */ +/* $OpenBSD: sys_process.c,v 1.103 2024/11/27 05:25:57 anton Exp $ */ /* $NetBSD: sys_process.c,v 1.55 1996/05/15 06:17:47 tls Exp $ */ /*- @@ -66,6 +66,7 @@ #include +#include #include #ifdef PTRACE @@ -206,6 +207,24 @@ sys_ptrace(struct proc *p, void *v, register_t *retval) mode = OUT; size = sizeof u.u_pacmask; break; +#endif +#ifdef PT_GETXSTATE_INFO + case PT_GETXSTATE_INFO: + mode = OUT_ALLOC; + size = sizeof(struct ptrace_xstate_info); + break; +#endif +#ifdef PT_GETXSTATE + case PT_GETXSTATE: + mode = OUT_ALLOC; + size = fpu_save_len; + break; +#endif +#ifdef PT_SETXSTATE + case PT_SETXSTATE: + mode = IN_ALLOC; + size = fpu_save_len; + break; #endif default: return EINVAL; @@ -759,6 +778,18 @@ ptrace_ustate(struct proc *p, int req, pid_t pid, void *addr, int data, ((register_t *)addr)[0] = process_get_pacmask(t); ((register_t *)addr)[1] = process_get_pacmask(t); return 0; +#endif +#ifdef PT_GETXSTATE_INFO + case PT_GETXSTATE_INFO: + return process_read_xstate_info(t, addr); +#endif +#ifdef PT_GETXSTATE + case PT_GETXSTATE: + return process_read_xstate(t, addr); +#endif +#ifdef PT_SETXSTATE + case PT_SETXSTATE: + return process_write_xstate(t, addr); #endif default: KASSERTMSG(0, "%s: unhandled request %d", __func__, req);