Index: sys/amd64/amd64/elf_machdep.c =================================================================== --- sys/amd64/amd64/elf_machdep.c +++ sys/amd64/amd64/elf_machdep.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -152,6 +153,44 @@ (sysinit_cfunc_t) elf64_insert_brand_entry, &kfreebsd_brand_info); +static regset_get x86_xstate_get; +static struct regset x86_xstate = { + .note = NT_X86_XSTATE, + .size = 0, + .align = _Alignof(uint64_t), + .get = &x86_xstate_get, +}; +ELF_REGSET(x86_xstate); + +static void +x86_xstate_update(void *arg __unused) +{ + + x86_xstate.size = cpu_max_ext_state_size; +} +SYSINIT(x86_xstate_update, SI_SUB_DRIVERS, SI_ORDER_ANY, x86_xstate_update, + NULL); + +static bool +x86_xstate_get(struct regset *rs, struct thread *td, void *dst, size_t *sizep) +{ + + if (!use_xsave) + return (false); + + KASSERT(*sizep >= cpu_max_ext_state_size, + ("x86_xstate_get: Size is too small (%zu < %u)", *sizep, + cpu_max_ext_state_size)); + *sizep = cpu_max_ext_state_size; + if (dst != NULL) { + fpugetregs(td); + memcpy(dst, get_pcb_user_save_td(td), cpu_max_ext_state_size); + *(uint64_t *)((char *)dst + X86_XSTATE_XCR0_OFFSET) = + xsave_mask; + } + return (true); +} + void elf64_dump_thread(struct thread *td, void *dst, size_t *off) { Index: sys/kern/sys_process.c =================================================================== --- sys/kern/sys_process.c +++ sys/kern/sys_process.c @@ -88,6 +88,11 @@ }; #endif +struct ptrace_regset { + void *prs_addr; + size_t prs_size; +}; + /* * Functions implemented using PROC_ACTION(): * @@ -171,6 +176,47 @@ PROC_ACTION(set_fpregs(td, fpregs)); } +SET_DECLARE(elf_regset, struct regset); +static size_t +proc_regset_maxsize(int note) +{ + struct regset **regsetpp, *regsetp; + + SET_FOREACH(regsetpp, elf_regset) { + regsetp = *regsetpp; + if (regsetp->note != note) + continue; + + return (regsetp->size); + } + + return (0); +} + +static int +proc_read_regset(struct thread *td, int note, struct iovec *vec) +{ + struct regset **regsetpp, *regsetp; + size_t size; + int err; + + SET_FOREACH(regsetpp, elf_regset) { + regsetp = *regsetpp; + if (regsetp->note != note) + continue; + + err = 0; + size = vec->iov_len; + if (!regsetp->get(regsetp, td, vec->iov_base, &size)) + err = EINVAL; + KASSERT(size == vec->iov_len, + ("proc_read_regset: Get function changed the size")); + + return (err); + } + return (EINVAL); +} + #ifdef COMPAT_FREEBSD32 /* For 32 bit binaries, we need to expose the 32 bit regs layouts. */ int @@ -568,6 +614,7 @@ struct dbreg dbreg; struct fpreg fpreg; struct reg reg; + struct iovec vec; #ifdef COMPAT_FREEBSD32 struct dbreg32 dbreg32; struct fpreg32 fpreg32; @@ -579,6 +626,7 @@ char args[sizeof(td->td_sa.args)]; int ptevents; } r; + struct iovec kern_vec; void *addr; int error = 0; #ifdef COMPAT_FREEBSD32 @@ -591,6 +639,8 @@ AUDIT_ARG_CMD(uap->req); AUDIT_ARG_VALUE(uap->data); addr = &r; + kern_vec.iov_base = NULL; + kern_vec.iov_len = 0; switch (uap->req) { case PT_GET_EVENT_MASK: case PT_LWPINFO: @@ -605,6 +655,25 @@ case PT_GETDBREGS: BZERO(&r.dbreg, sizeof r.dbreg); break; + case PT_GETREGSET: + if (wrap32) { + /* TODO: compat32 support */ + error = EINVAL; + break; + } + + error = copyin(uap->addr, &r.vec, sizeof r.vec); + if (error != 0) + break; + kern_vec.iov_len = proc_regset_maxsize(uap->data); + if (kern_vec.iov_len == 0) { + error = EINVAL; + break; + } + kern_vec.iov_base = malloc(kern_vec.iov_len, M_TEMP, + M_WAITOK | M_ZERO); + addr = &kern_vec; + break; case PT_SETREGS: error = COPYIN(uap->addr, &r.reg, sizeof r.reg); break; @@ -634,8 +703,10 @@ return (error); error = kern_ptrace(td, uap->req, uap->pid, addr, uap->data); - if (error) + if (error) { + free(kern_vec.iov_base, M_TEMP); return (error); + } switch (uap->req) { case PT_VM_ENTRY: @@ -653,6 +724,16 @@ case PT_GETDBREGS: error = COPYOUT(&r.dbreg, uap->addr, sizeof r.dbreg); break; + case PT_GETREGSET: + r.vec.iov_len = MIN(r.vec.iov_len, kern_vec.iov_len); + error = copyout(kern_vec.iov_base, r.vec.iov_base, + r.vec.iov_len); + if (error != 0) { + break; + } + error = copyout(&r.vec, uap->addr, sizeof r.vec); + free(kern_vec.iov_base, M_TEMP); + break; case PT_GET_EVENT_MASK: /* NB: The size in uap->data is validated in kern_ptrace(). */ error = copyout(&r.ptevents, uap->addr, uap->data); @@ -1319,6 +1400,12 @@ error = PROC_READ(dbregs, td2, addr); break; + case PT_GETREGSET: + CTR2(KTR_PTRACE, "PT_GETREGSET: tid %d (pid %d)", td2->td_tid, + p->p_pid); + error = proc_read_regset(td2, data, addr); + break; + case PT_LWPINFO: if (data <= 0 || #ifdef COMPAT_FREEBSD32 Index: sys/sys/ptrace.h =================================================================== --- sys/sys/ptrace.h +++ sys/sys/ptrace.h @@ -37,6 +37,7 @@ #include #include +#include #include #define PT_TRACE_ME 0 /* child declares it's being traced */ @@ -83,6 +84,9 @@ #define PT_VM_TIMESTAMP 40 /* Get VM version (timestamp) */ #define PT_VM_ENTRY 41 /* Get VM map (entry) */ +#define PT_GETREGSET 42 /* Get a target register set */ +/*#define PT_SETREGSET 43 */ /* Set a target register set */ + #define PT_FIRSTMACH 64 /* for machine-specific requests */ #include /* machine-specific requests, if any */ Index: sys/sys/reg.h =================================================================== --- sys/sys/reg.h +++ sys/sys/reg.h @@ -5,6 +5,20 @@ #include #ifdef _KERNEL +struct regset; + +typedef bool (regset_get)(struct regset *, struct thread *, void *, + size_t *); + +struct regset { + int note; + size_t size; + size_t align; + regset_get *get; +}; + +#define ELF_REGSET(regset) DATA_SET(elf_regset, regset) + int fill_regs(struct thread *, struct reg *); int set_regs(struct thread *, struct reg *); int fill_fpregs(struct thread *, struct fpreg *);