1
0
mirror of https://github.com/openbsd/src.git synced 2024-12-22 07:27:59 -08:00

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@
This commit is contained in:
anton 2024-11-27 05:25:56 +00:00
parent 484eec8f97
commit deef986e6c
4 changed files with 190 additions and 5 deletions

View File

@ -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

View File

@ -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 <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <uvm/uvm_extern.h>
@ -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 */

View File

@ -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

View File

@ -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 <uvm/uvm_extern.h>
#include <machine/fpu.h>
#include <machine/reg.h>
#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);