Skip to content
Permalink
Browse files

Implement PT_GETXSTATE and PT_SETXSTATE

Introduce two new ptrace() requests: PT_GETXSTATE and PT_SETXSTATE,
that provide access to the extended (and extensible) set of FPU
registers on amd64 and i386.  At the moment, this covers AVX (YMM)
and AVX-512 (ZMM, opmask) registers.  It can be easily extended
to cover further register types without breaking backwards
compatibility.

PT_GETXSTATE issues the XSAVE instruction with all kernel-supported
extended components enabled.  The data is copied into 'struct xstate'
(which -- unlike the XSAVE area itself -- has stable format
and offsets).

PT_SETXSTATE issues the XRSTOR instruction to restore the register
values from user-provided 'struct xstate'.  The function replaces only
the specific XSAVE components that are listed in 'xs_rfbm' field,
making it possible to issue partial updates.

Both syscalls take a 'struct iovec' pointer rather than a direct
argument.  This requires the caller to explicitly specify the buffer
size.  As a result, existing code will continue to work correctly
when the structure is extended (performing partial reads/updates).
  • Loading branch information...
mgorny committed Jun 26, 2019
1 parent 581c569 commit be907e72edaf1f27cb9717ac68139b84c4eb699c
@@ -1,7 +1,7 @@
.\" $NetBSD: ptrace.2,v 1.74 2019/06/12 12:33:42 wiz Exp $
.\" $NetBSD: ptrace.2,v 1.75 2019/06/26 12:30:12 mgorny Exp $
.\"
.\" This file is in the public domain.
.Dd June 12, 2019
.Dd June 26, 2019
.Dt PTRACE 2
.Os
.Sh NAME
@@ -771,6 +771,69 @@ The
argument contains the LWP ID of the thread whose registers are to
be written.
If zero is supplied, the first thread of the process is written.
.It Dv PT_GETXSTATE
This request reads the traced process' FPU extended state into
the
.Dq Li "struct xstate"
(defined in
.In machine/cpu_extended_state.h ) .
.Fa addr
should be a pointer to
.Dq Li "struct iovec"
(defined in
.In sys/uio.h )
specifying the pointer to the aforementioned struct as
.Fa iov_base
and its size as
.Fa iov_len .
The
.Fa data
argument contains the LWP ID of the thread whose registers are to
be read.
If zero is supplied, the first thread of the process is read.
The struct will be filled up to the specified
.Fa iov_len .
The caller needs to check
.Fa xs_rfbm
bitmap in order to determine which fields were provided by the CPU,
and may check
.Fa xs_xstate_bv
to determine which component states were changed from the initial state.
.It Dv PT_SETXSTATE
This request is the converse of
.Dv PT_GETXSTATE ;
it loads the traced process' extended FPU state from the
.Dq Li "struct xstate"
(defined in
.In machine/cpu_extended_state.h ) .
.Fa addr
should be a pointer to
.Dq Li "struct iovec"
(defined in
.In sys/uio.h )
specifying the pointer to the aforementioned struct as
.Fa iov_base
and its size as
.Fa iov_len .
The
.Fa data
argument contains the LWP ID of the thread whose registers are to
be written.
If zero is supplied, the first thread of the process is written.
The
.Fa xs_rfbm
field of the supplied xstate specifies which state components are to
be updated. Other components (fields) will be ignored. The
.Fa xs_xstate_bv
specifies whether component state should be set to provided values
(when 1) or reset to unitialized (when 0). The request
will fail if
.Fa xs_xstate_bv
is not a subset of
.Fa xs_rfbm ,
or any of the specified components is not supported by the CPU or kernel
(i.e. not returned by
.Dv PT_GETXSTATE .
.El
.Sh ERRORS
Some requests can cause
@@ -819,8 +882,10 @@ was neither 0 nor a legal signal number.
.Dv PT_GETREGS ,
.Dv PT_SETREGS ,
.Dv PT_GETFPREGS ,
.Dv PT_SETFPREGS ,
.Dv PT_GETXSTATE ,
or
.Dv PT_SETFPREGS
.Dv PT_SETXSTATE
was attempted on a process with no valid register set.
(This is normally true only of system processes.)
.It
@@ -832,6 +897,13 @@ or
with
.Dv vm.user_va0_disable
set to 1.
.It
.Dv PT_SETXSTATE
attempted to set state components not supported by the kernel,
or
.Dv xs_xstate_bv
was not a subset of
.Dv xs_rfbm .
.El
.It Bq Er EPERM
.Bl -bullet -compact
@@ -1,4 +1,4 @@
/* $NetBSD: netbsd32_machdep.c,v 1.123 2019/06/04 16:30:19 mgorny Exp $ */
/* $NetBSD: netbsd32_machdep.c,v 1.124 2019/06/26 12:30:12 mgorny Exp $ */

/*
* Copyright (c) 2001 Wasabi Systems, Inc.
@@ -36,7 +36,7 @@
*/

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: netbsd32_machdep.c,v 1.123 2019/06/04 16:30:19 mgorny Exp $");
__KERNEL_RCSID(0, "$NetBSD: netbsd32_machdep.c,v 1.124 2019/06/26 12:30:12 mgorny Exp $");

#ifdef _KERNEL_OPT
#include "opt_compat_netbsd.h"
@@ -353,6 +353,8 @@ netbsd32_ptrace_translate_request(int req)
case PT32_SETDBREGS: return PT_SETDBREGS;
case PT32_SETSTEP: return PT_SETSTEP;
case PT32_CLEARSTEP: return PT_CLEARSTEP;
case PT32_GETXSTATE: return PT_GETXSTATE;
case PT32_SETXSTATE: return PT_SETXSTATE;
default: return -1;
}
}
@@ -1,4 +1,4 @@
/* $NetBSD: process_machdep.c,v 1.39 2019/02/11 14:59:32 cherry Exp $ */
/* $NetBSD: process_machdep.c,v 1.40 2019/06/26 12:30:12 mgorny Exp $ */

/*
* Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
@@ -74,7 +74,7 @@
*/

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.39 2019/02/11 14:59:32 cherry Exp $");
__KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.40 2019/06/26 12:30:12 mgorny Exp $");

#include "opt_xen.h"
#include <sys/param.h>
@@ -84,6 +84,9 @@ __KERNEL_RCSID(0, "$NetBSD: process_machdep.c,v 1.39 2019/02/11 14:59:32 cherry
#include <sys/proc.h>
#include <sys/ptrace.h>

#include <uvm/uvm_extern.h>

#include <compat/netbsd32/netbsd32.h>
#include <machine/psl.h>
#include <machine/reg.h>
#include <machine/segments.h>
@@ -288,3 +291,131 @@ process_set_pc(struct lwp *l, void *addr)

return 0;
}

#ifdef __HAVE_PTRACE_MACHDEP
static int
process_machdep_read_xstate(struct lwp *l, struct xstate *regs)
{
return process_read_xstate(l, regs);
}

static int
process_machdep_write_xstate(struct lwp *l, const struct xstate *regs)
{
int error;

/*
* Check for security violations.
*/
error = process_verify_xstate(regs);
if (error != 0)
return error;

return process_write_xstate(l, regs);
}

int
ptrace_machdep_dorequest(
struct lwp *l,
struct lwp *lt,
int req,
void *addr,
int data
)
{
struct uio uio;
struct iovec iov;
struct vmspace *vm;
int error;
int write = 0;

switch (req) {
case PT_SETXSTATE:
write = 1;

/* FALLTHROUGH */
case PT_GETXSTATE:
/* write = 0 done above. */
if (!process_machdep_validxstate(lt->l_proc))
return EINVAL;
if (__predict_false(l->l_proc->p_flag & PK_32)) {
struct netbsd32_iovec *user_iov;
user_iov = (struct netbsd32_iovec*)addr;
iov.iov_base = NETBSD32PTR64(user_iov->iov_base);
iov.iov_len = user_iov->iov_len;
} else {
struct iovec *user_iov;
user_iov = (struct iovec*)addr;
iov.iov_base = user_iov->iov_base;
iov.iov_len = user_iov->iov_len;
}

error = proc_vmspace_getref(l->l_proc, &vm);
if (error)
return error;
if (iov.iov_len > sizeof(struct xstate))
iov.iov_len = sizeof(struct xstate);
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_offset = 0;
uio.uio_resid = iov.iov_len;
uio.uio_rw = write ? UIO_WRITE : UIO_READ;
uio.uio_vmspace = vm;
error = process_machdep_doxstate(l, lt, &uio);
uvmspace_free(vm);
return error;
}

#ifdef DIAGNOSTIC
panic("ptrace_machdep: impossible");
#endif

return 0;
}

/*
* The following functions are used by both ptrace(2) and procfs.
*/

int
process_machdep_doxstate(struct lwp *curl, struct lwp *l, struct uio *uio)
/* curl: tracer */
/* l: traced */
{
int error;
struct xstate r;
char *kv;
ssize_t kl;

memset(&r, 0, sizeof(r));
kl = MIN(uio->uio_iov->iov_len, sizeof(r));
kv = (char *) &r;

kv += uio->uio_offset;
kl -= uio->uio_offset;
if (kl > uio->uio_resid)
kl = uio->uio_resid;

if (kl < 0)
error = EINVAL;
else
error = process_machdep_read_xstate(l, &r);
if (error == 0)
error = uiomove(kv, kl, uio);
if (error == 0 && uio->uio_rw == UIO_WRITE)
error = process_machdep_write_xstate(l, &r);

uio->uio_offset = 0;
return error;
}

int
process_machdep_validxstate(struct proc *p)
{

if (p->p_flag & PK_SYSTEM)
return 0;

return 1;
}
#endif /* __HAVE_PTRACE_MACHDEP */
@@ -1,4 +1,4 @@
/* $NetBSD: netbsd32_machdep.h,v 1.23 2019/06/04 16:29:53 mgorny Exp $ */
/* $NetBSD: netbsd32_machdep.h,v 1.24 2019/06/26 12:30:12 mgorny Exp $ */

#ifndef _MACHINE_NETBSD32_H_
#define _MACHINE_NETBSD32_H_
@@ -22,6 +22,8 @@
#define PT32_SETDBREGS (PT_FIRSTMACH + 8)
#define PT32_SETSTEP (PT_FIRSTMACH + 9)
#define PT32_CLEARSTEP (PT_FIRSTMACH + 10)
#define PT32_GETXSTATE (PT_FIRSTMACH + 11)
#define PT32_SETXSTATE (PT_FIRSTMACH + 12)

#define NETBSD32_POINTER_TYPE uint32_t
typedef struct { NETBSD32_POINTER_TYPE i32; } netbsd32_pointer_t;
@@ -1,4 +1,4 @@
/* $NetBSD: ptrace.h,v 1.15 2019/06/18 21:18:11 kamil Exp $ */
/* $NetBSD: ptrace.h,v 1.16 2019/06/26 12:30:12 mgorny Exp $ */

/*
* Copyright (c) 1993 Christopher G. Demetriou
@@ -45,6 +45,11 @@
#define PT_SETDBREGS (PT_FIRSTMACH + 6)
#define PT_SETSTEP (PT_FIRSTMACH + 7)
#define PT_CLEARSTEP (PT_FIRSTMACH + 8)
#define PT_GETXSTATE (PT_FIRSTMACH + 9)
#define PT_SETXSTATE (PT_FIRSTMACH + 10)

/* We have machine-dependent process tracing needs. */
#define __HAVE_PTRACE_MACHDEP

#define PT_MACHDEP_STRINGS \
"PT_STEP", \
@@ -55,7 +60,9 @@
"PT_GETDBREGS", \
"PT_SETDBREGS", \
"PT_SETSTEP", \
"PT_CLEARSTEP",
"PT_CLEARSTEP", \
"PT_GETXSTATE", \
"PT_SETXSTATE"

#include <machine/reg.h>
#define PTRACE_REG_PC(r) (r)->regs[_REG_RIP]
@@ -71,6 +78,20 @@
#define PTRACE_BREAKPOINT_SIZE 1
#define PTRACE_BREAKPOINT_ADJ 1

#ifdef _KERNEL

/*
* These are used in sys_ptrace() to find good ptrace(2) requests.
*/
#define PTRACE_MACHDEP_REQUEST_CASES \
case PT_GETXSTATE: \
case PT_SETXSTATE:

int process_machdep_doxstate(struct lwp *, struct lwp *, struct uio *);
int process_machdep_validxstate(struct proc *);

#endif /* _KERNEL */

#ifdef _KERNEL_OPT
#include "opt_compat_netbsd32.h"

0 comments on commit be907e7

Please sign in to comment.
You can’t perform that action at this time.