# /home/anton/tmp/robsd/src-ptrace-xstate.diff diff --git gnu/llvm/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86_64.cpp gnu/llvm/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86_64.cpp index b05dde6001ad..26b28bf42dfe 100644 --- gnu/llvm/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86_64.cpp +++ gnu/llvm/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86_64.cpp @@ -9,14 +9,14 @@ #if defined(__x86_64__) +#include "NativeRegisterContextOpenBSD_x86_64.h" +#include #include #include #include #include -#include "NativeRegisterContextOpenBSD_x86_64.h" - #include "lldb/Host/HostInfo.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Log.h" @@ -24,9 +24,11 @@ #include "lldb/Utility/Status.h" #include "Plugins/Process/Utility/RegisterContextOpenBSD_x86_64.h" +#include "Plugins/Process/Utility/RegisterContext_x86.h" // clang-format off #include +#include #include #include #include @@ -92,8 +94,20 @@ static_assert( == k_num_fpr_registers_x86_64, "g_fpu_regnums_x86_64 has wrong number of register infos"); +static const uint32_t g_avx_regnums_x86_64[] = { + lldb_ymm0_x86_64, lldb_ymm1_x86_64, lldb_ymm2_x86_64, lldb_ymm3_x86_64, + lldb_ymm4_x86_64, lldb_ymm5_x86_64, lldb_ymm6_x86_64, lldb_ymm7_x86_64, + lldb_ymm8_x86_64, lldb_ymm9_x86_64, lldb_ymm10_x86_64, lldb_ymm11_x86_64, + lldb_ymm12_x86_64, lldb_ymm13_x86_64, lldb_ymm14_x86_64, lldb_ymm15_x86_64, + LLDB_INVALID_REGNUM // register sets need to end with this flag +}; +static_assert( + (sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) - 1 + == k_num_avx_registers_x86_64, + "g_avx_regnums_x86_64 has wrong number of register infos"); + // Number of register sets provided by this context. -enum { k_num_register_sets = 2 }; +enum { k_num_register_sets = 3 }; // Register sets for x86 64-bit. static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = { @@ -101,6 +115,8 @@ static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = { g_gpr_regnums_x86_64}, {"Floating Point Registers", "fpu", k_num_fpr_registers_x86_64, g_fpu_regnums_x86_64}, + {"Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64, + g_avx_regnums_x86_64}, }; struct x86_fpu_addr { @@ -108,6 +124,11 @@ struct x86_fpu_addr { uint32_t selector; }; +enum { + k_xsave_offset_legacy_region = 160, + k_xsave_offset_invalid = UINT32_MAX, +}; + } // namespace #define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize()) @@ -135,7 +156,18 @@ NativeRegisterContextOpenBSD_x86_64::NativeRegisterContextOpenBSD_x86_64( const ArchSpec &target_arch, NativeThreadProtocol &native_thread) : NativeRegisterContextOpenBSD(native_thread, CreateRegisterInfoInterface(target_arch)), - m_gpr(), m_fpr() {} + m_gpr(), m_fpr() { + uint32_t a, b, c, d; + + struct ptrace_xstate_info info; + const Status error = NativeProcessOpenBSD::PtraceWrapper( + PT_GETXSTATE_INFO, GetProcessPid(), &info, sizeof(info)); + if (error.Success()) + m_xsave.resize(info.xsave_len); + + __get_cpuid_count(0xd, 2, &a, &b, &c, &d); + m_xsave_offsets[YMMRegSet] = b > 0 ? b : k_xsave_offset_invalid; +} uint32_t NativeRegisterContextOpenBSD_x86_64::GetUserRegisterCount() const { uint32_t count = 0; @@ -167,6 +199,8 @@ int NativeRegisterContextOpenBSD_x86_64::GetSetForNativeRegNum( return GPRegSet; else if (reg_num >= k_first_fpr_x86_64 && reg_num <= k_last_fpr_x86_64) return FPRegSet; + else if (reg_num >= k_first_avx_x86_64 && reg_num <= k_last_avx_x86_64) + return YMMRegSet; else return -1; } @@ -179,6 +213,11 @@ int NativeRegisterContextOpenBSD_x86_64::ReadRegisterSet(uint32_t set) { case FPRegSet: ReadFPR(); return 0; + case YMMRegSet: { + const Status error = NativeProcessOpenBSD::PtraceWrapper( + PT_GETXSTATE, GetProcessPid(), m_xsave.data(), m_xsave.size()); + return error.Success() ? 0 : -1; + } default: break; } @@ -192,6 +231,11 @@ int NativeRegisterContextOpenBSD_x86_64::WriteRegisterSet(uint32_t set) { case FPRegSet: WriteFPR(); return 0; + case YMMRegSet: { + const Status error = NativeProcessOpenBSD::PtraceWrapper( + PT_SETXSTATE, GetProcessPid(), m_xsave.data(), m_xsave.size()); + return error.Success() ? 0 : -1; + } default: break; } @@ -393,6 +437,18 @@ NativeRegisterContextOpenBSD_x86_64::ReadRegister(const RegisterInfo *reg_info, break; } + if (set == YMMRegSet) { + std::optional ymm_reg = GetYMMSplitReg(reg); + if (ymm_reg) { + YMMReg ymm = XStateToYMM(ymm_reg->xmm, ymm_reg->ymm_hi); + reg_value.SetBytes(ymm.bytes, reg_info->byte_size, + endian::InlHostByteOrder()); + } else { + error.SetErrorStringWithFormat("register \"%s\" not supported", + reg_info->name); + } + } + return error; } @@ -591,6 +647,18 @@ Status NativeRegisterContextOpenBSD_x86_64::WriteRegister( break; } + if (set == YMMRegSet) { + std::optional ymm_reg = GetYMMSplitReg(reg); + if (!ymm_reg) { + error.SetErrorStringWithFormat("register \"%s\" not supported", + reg_info->name); + return error; + } + YMMReg ymm; + ::memcpy(ymm.bytes, reg_value.GetBytes(), reg_value.GetByteSize()); + YMMToXState(ymm, ymm_reg->xmm, ymm_reg->ymm_hi); + } + if (WriteRegisterSet(set) != 0) error.SetErrorStringWithFormat("failed to write register set"); @@ -674,4 +742,18 @@ Status NativeRegisterContextOpenBSD_x86_64::WriteAllRegisterValues( return error; } + +std::optional +NativeRegisterContextOpenBSD_x86_64::GetYMMSplitReg(uint32_t reg) { + if (m_xsave_offsets[YMMRegSet] == k_xsave_offset_invalid) + return std::nullopt; + + uint32_t reg_index = reg - lldb_ymm0_x86_64; + auto *xmm = + reinterpret_cast(m_xsave.data() + k_xsave_offset_legacy_region); + auto *ymm = + reinterpret_cast(m_xsave.data() + m_xsave_offsets[YMMRegSet]); + return YMMSplitPtr{&xmm[reg_index], &ymm[reg_index]}; +} + #endif diff --git gnu/llvm/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86_64.h gnu/llvm/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86_64.h index 60e13da63474..0be2a8b36921 100644 --- gnu/llvm/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86_64.h +++ gnu/llvm/lldb/source/Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD_x86_64.h @@ -15,6 +15,9 @@ #include // clang-format on +#include +#include + #include "Plugins/Process/OpenBSD/NativeRegisterContextOpenBSD.h" #include "Plugins/Process/Utility/RegisterContext_x86.h" #include "Plugins/Process/Utility/lldb-x86-register-enums.h" @@ -52,16 +55,29 @@ protected: private: // Private member types. - enum { GPRegSet, FPRegSet }; + enum { + GPRegSet, + FPRegSet, + YMMRegSet, + MaxRegSet = YMMRegSet, + }; // Private member variables. struct reg m_gpr; struct fpreg m_fpr; + std::vector m_xsave; + std::array m_xsave_offsets; int GetSetForNativeRegNum(int reg_num) const; int ReadRegisterSet(uint32_t set); int WriteRegisterSet(uint32_t set); + + struct YMMSplitPtr { + void *xmm; + void *ymm_hi; + }; + std::optional GetYMMSplitReg(uint32_t reg); }; } // namespace process_openbsd diff --git lib/libc/sys/ptrace.2 lib/libc/sys/ptrace.2 index 06dd079a9804..321afff18172 100644 --- lib/libc/sys/ptrace.2 +++ lib/libc/sys/ptrace.2 @@ -500,6 +500,57 @@ 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. +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 @@ -548,6 +599,9 @@ or .Dv PT_SETFPREGS was attempted on a process with no valid register set. (This is normally true only of system processes.) +.It +.Dv PT_SETXSTATE +was attempted with an invalid XSAVE area. .El .It Bq Er EBUSY .Bl -bullet -compact @@ -579,6 +633,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 regress/sys/kern/ptrace/Makefile regress/sys/kern/ptrace/Makefile index 5a41ee05a492..f455aa143911 100644 --- regress/sys/kern/ptrace/Makefile +++ regress/sys/kern/ptrace/Makefile @@ -1,5 +1,7 @@ # $OpenBSD: Makefile,v 1.3 2016/09/23 18:56:49 bluhm Exp $ +SUBDIR+= xstate + PROG= ptrace read_i: diff --git regress/sys/kern/ptrace/xstate/Makefile regress/sys/kern/ptrace/xstate/Makefile new file mode 100644 index 000000000000..6b4d3cd5ded8 --- /dev/null +++ regress/sys/kern/ptrace/xstate/Makefile @@ -0,0 +1,27 @@ +# $OpenBSD$ + +.if ${MACHINE} == "amd64" + +WARNINGS= yes + +PROG= xstate +SRCS+= xstate.c +SRCS+= avx.S + +REGRESS_SETUP_ONCE=${PROG} + +.for t in xstate-bad-header \ + xstate-ymm-get xstate-ymm-set +${t}: + ${.OBJDIR}/xstate ${t} +REGRESS_TARGETS+=${t} +.endfor + +.else + +regress: + @echo SKIPPED + +.endif + +.include diff --git regress/sys/kern/ptrace/xstate/avx.S regress/sys/kern/ptrace/xstate/avx.S new file mode 100644 index 000000000000..5571a2e634e2 --- /dev/null +++ regress/sys/kern/ptrace/xstate/avx.S @@ -0,0 +1,79 @@ +/* $OpenBSD */ + +#include + + .intel_syntax noprefix + +/* void ymm_write(void) */ +ENTRY(ymm_write) + vmovdqu ymm0, [rip + .Lymm0] + vmovdqu ymm1, [rip + .Lymm1] + vmovdqu ymm2, [rip + .Lymm2] + vmovdqu ymm3, [rip + .Lymm3] + vmovdqu ymm4, [rip + .Lymm4] + vmovdqu ymm5, [rip + .Lymm5] + vmovdqu ymm6, [rip + .Lymm6] + vmovdqu ymm7, [rip + .Lymm7] + vmovdqu ymm8, [rip + .Lymm8] + vmovdqu ymm9, [rip + .Lymm9] + vmovdqu ymm10, [rip + .Lymm10] + vmovdqu ymm11, [rip + .Lymm11] + vmovdqu ymm12, [rip + .Lymm12] + vmovdqu ymm13, [rip + .Lymm13] + vmovdqu ymm14, [rip + .Lymm14] + vmovdqu ymm15, [rip + .Lymm15] + ret + +/* void ymm_read(struct ymm[16]) */ +ENTRY(ymm_read) + vmovdqu [rdi + 0x000], ymm0 + vmovdqu [rdi + 0x020], ymm1 + vmovdqu [rdi + 0x040], ymm2 + vmovdqu [rdi + 0x060], ymm3 + vmovdqu [rdi + 0x080], ymm4 + vmovdqu [rdi + 0x0a0], ymm5 + vmovdqu [rdi + 0x0c0], ymm6 + vmovdqu [rdi + 0x0e0], ymm7 + vmovdqu [rdi + 0x100], ymm8 + vmovdqu [rdi + 0x120], ymm9 + vmovdqu [rdi + 0x140], ymm10 + vmovdqu [rdi + 0x160], ymm11 + vmovdqu [rdi + 0x180], ymm12 + vmovdqu [rdi + 0x1a0], ymm13 + vmovdqu [rdi + 0x1c0], ymm14 + vmovdqu [rdi + 0x1e0], ymm15 + ret + + .rodata +.Lymm0: + .rept 4; .quad 0x0000000000000000; .endr +.Lymm1: + .rept 4; .quad 0x1111111111111111; .endr +.Lymm2: + .rept 4; .quad 0x2222222222222222; .endr +.Lymm3: + .rept 4; .quad 0x3333333333333333; .endr +.Lymm4: + .rept 4; .quad 0x4444444444444444; .endr +.Lymm5: + .rept 4; .quad 0x5555555555555555; .endr +.Lymm6: + .rept 4; .quad 0x6666666666666666; .endr +.Lymm7: + .rept 4; .quad 0x7777777777777777; .endr +.Lymm8: + .rept 4; .quad 0x8888888888888888; .endr +.Lymm9: + .rept 4; .quad 0x9999999999999999; .endr +.Lymm10: + .rept 4; .quad 0xaaaaaaaaaaaaaaaa; .endr +.Lymm11: + .rept 4; .quad 0xbbbbbbbbbbbbbbbb; .endr +.Lymm12: + .rept 4; .quad 0xcccccccccccccccc; .endr +.Lymm13: + .rept 4; .quad 0xdddddddddddddddd; .endr +.Lymm14: + .rept 4; .quad 0xeeeeeeeeeeeeeeee; .endr +.Lymm15: + .rept 4; .quad 0xffffffffffffffff; .endr diff --git regress/sys/kern/ptrace/xstate/xstate.c regress/sys/kern/ptrace/xstate/xstate.c new file mode 100644 index 000000000000..3c4c03318a03 --- /dev/null +++ regress/sys/kern/ptrace/xstate/xstate.c @@ -0,0 +1,286 @@ +/* $OpenBSD */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct cpuid { + uint32_t a, b, c, d; +}; + +struct xstate { + struct { + uint8_t buf[1024]; + uint32_t size; + } area; + + struct { + uint32_t supported; + uint32_t offset; + uint32_t size; + } components[3]; +#define XSTATE_COMPONENT_X87 0 +#define XSTATE_COMPONENT_SSE 1 +#define XSTATE_COMPONENT_AVX 2 +}; + +struct u128 { + uint64_t v[2]; +} __attribute__((packed)); + +struct ymm { + struct u128 xmm; + struct u128 ymm; +} __attribute__((packed)); + +extern void ymm_write(void); +extern void ymm_read(struct ymm[16]); + +static inline void +cpuid(uint32_t leaf, uint32_t subleaf, struct cpuid *out) +{ + __asm__("cpuid" + : "=a" (out->a), "=b" (out->b), "=c" (out->c), "=d" (out->d) + : "a" (leaf), "c" (subleaf)); +} + +static int +xstate_init(struct xstate *xstate, pid_t pid) +{ +#define CPUID_01_C_XSAVE_MASK (1 << 26) +#define XCR0_XMM_MASK (1 << 1) +#define XCR0_YMM_MASK (1 << 2) + + struct cpuid leaf; + struct ptrace_xstate_info info; + + cpuid(0x1, 0, &leaf); + if ((leaf.c & CPUID_01_C_XSAVE_MASK) == 0) { + printf("SKIPPED: XSAVE not enumerated"); + return 1; + } + + memset(xstate, 0, sizeof(*xstate)); + + if (ptrace(PT_GETXSTATE_INFO, pid, + (caddr_t)&info, sizeof(info)) == -1) + err(1, "ptrace: PT_GETXSTATE_INFO"); + if (info.xsave_len > sizeof(xstate->area.buf)) + errx(1, "xstate buffer too small"); + xstate->area.size = info.xsave_len; + + if ((info.xsave_mask & XCR0_XMM_MASK) == 0 || + (info.xsave_mask & XCR0_YMM_MASK) == 0) { + printf("SKIPPED: SSE/AVX disabled in XCR0\n"); + return 1; + } + + xstate->components[XSTATE_COMPONENT_SSE].supported = 1; + /* Part of legacy region in XSAVE area. */ + xstate->components[XSTATE_COMPONENT_SSE].offset = 160; + xstate->components[XSTATE_COMPONENT_SSE].size = 256; + + cpuid(0xd, XSTATE_COMPONENT_AVX, &leaf); + xstate->components[XSTATE_COMPONENT_AVX].supported = 1; + xstate->components[XSTATE_COMPONENT_AVX].offset = leaf.b; + xstate->components[XSTATE_COMPONENT_AVX].size = leaf.a; + + return 0; +} + +static void +xstate_ymm_read(struct xstate *xstate, int regno, struct ymm *rd) +{ + struct u128 *xmm = (struct u128 *)(xstate->area.buf + + xstate->components[XSTATE_COMPONENT_SSE].offset); + struct u128 *ymm = (struct u128 *)(xstate->area.buf + + xstate->components[XSTATE_COMPONENT_AVX].offset); + + rd->xmm = xmm[regno]; + rd->ymm = ymm[regno]; +} + +static void +xstate_ymm_write(struct xstate *xstate, int regno, struct ymm *wr) +{ + struct u128 *xmm = (struct u128 *)(xstate->area.buf + + xstate->components[XSTATE_COMPONENT_SSE].offset); + struct u128 *ymm = (struct u128 *)(xstate->area.buf + + xstate->components[XSTATE_COMPONENT_AVX].offset); + + xmm[regno] = wr->xmm; + ymm[regno] = wr->ymm; +} + +static void +wait_until_stopped(pid_t pid) +{ + int status; + + if (waitpid(pid, &status, 0) == -1) + err(1, "waitpid"); + if (!WIFSTOPPED(status)) + errx(1, "expected traced process to be stopped"); +} + +static int +check_ymm(const struct ymm ymm[16]) +{ + int error = 0; + int i; + + for (i = 0; i < 16; i++) { + struct ymm exp; + + memset(&exp, (i << 4) | i, 32); + if (memcmp(&exp, &ymm[i], 32) == 0) + continue; + + warnx("ymm%d: expected %016llx%016llx%016llx%016llx," + " got %016llx%016llx%016llx%016llx", i, + exp.ymm.v[1], exp.ymm.v[0], + exp.xmm.v[1], exp.xmm.v[0], + ymm[i].ymm.v[1], ymm[i].ymm.v[0], + ymm[i].xmm.v[1], ymm[i].xmm.v[0]); + error = 1; + } + + return error; +} + +static int +test_bad_header(struct xstate *xstate) +{ + char *xsave_header; + pid_t pid; + + pid = fork(); + if (pid == 0) { + ptrace(PT_TRACE_ME, 0, 0, 0); + raise(SIGSTOP); + /* UNREACHABLE */ + } + + wait_until_stopped(pid); + + if (ptrace(PT_GETXSTATE, pid, + xstate->area.buf, xstate->area.size) == -1) + err(1, "ptrace: PT_GETXSTATE"); + xsave_header = &xstate->area.buf[512]; + memset(xsave_header, 0xff, 64); + if (ptrace(PT_SETXSTATE, pid, + xstate->area.buf, xstate->area.size) != -1) + errx(1, "expected tampered header to be rejected"); + return 0; +} + +static int +test_ymm_get(struct xstate *xstate) +{ + struct ymm ymm[16]; + pid_t pid; + int i; + + pid = fork(); + if (pid == 0) { + ptrace(PT_TRACE_ME, 0, 0, 0); + ymm_write(); + raise(SIGSTOP); + /* UNREACHABLE */ + } + + wait_until_stopped(pid); + + if (xstate_init(xstate, pid)) + return 0; + + if (ptrace(PT_GETXSTATE, pid, + xstate->area.buf, xstate->area.size) == -1) + err(1, "ptrace: PT_GETXSTATE"); + for (i = 0; i < 16; i++) + xstate_ymm_read(xstate, i, &ymm[i]); + return check_ymm(ymm); +} + +static int +test_ymm_set(struct xstate *xstate) +{ + pid_t pid; + int i, status; + + pid = fork(); + if (pid == 0) { + struct ymm ymm[16]; + + ptrace(PT_TRACE_ME, 0, 0, 0); + raise(SIGSTOP); + ymm_read(ymm); + _exit(check_ymm(ymm)); + } + + wait_until_stopped(pid); + + if (xstate_init(xstate, pid)) + return 0; + + if (ptrace(PT_GETXSTATE, pid, + xstate->area.buf, xstate->area.size) == -1) + err(1, "ptrace: PT_GETXSTATE"); + for (i = 0; i < 16; i++) { + struct ymm ymm; + + memset(&ymm, (i << 4) | i, 32); + xstate_ymm_write(xstate, i, &ymm); + } + + if (ptrace(PT_SETXSTATE, pid, + xstate->area.buf, xstate->area.size) == -1) + err(1, "ptrace: PT_SETXSTATE"); + + if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) == -1) + err(1, "ptrace: PT_CONTINUE"); + if (waitpid(pid, &status, 0) == -1) + err(1, "waitpid"); + return WIFEXITED(status) && WEXITSTATUS(status) == 0 ? 0 : 1; +} + +static void __attribute__((noreturn)) +usage(void) +{ + fprintf(stderr, "usage: xstate test-case\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct { + const char *name; + int (*test)(struct xstate *); + } tests[] = { + { "xstate-bad-header", test_bad_header }, + { "xstate-ymm-get", test_ymm_get }, + { "xstate-ymm-set", test_ymm_set }, + }; + struct xstate xstate; + unsigned int i; + + if (argc != 2) + usage(); + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + if (strcmp(argv[1], tests[i].name) == 0) + return tests[i].test(&xstate); + } + + warnx("no such test case"); + return 1; +} diff --git sys/arch/amd64/amd64/process_machdep.c sys/arch/amd64/amd64/process_machdep.c index f6c27e4eb7e7..2f282a4ff137 100644 --- sys/arch/amd64/amd64/process_machdep.c +++ sys/arch/amd64/amd64/process_machdep.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -200,4 +201,71 @@ 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; +} __attribute__((packed)); + +int +process_read_xstate(struct proc *p, void *addr) +{ + struct xsave_area *area = + (struct xsave_area *)&p->p_addr->u_pcb.pcb_savefpu; + unsigned int i, ncomponents; + + if (xsave_mask == 0) + return (ENOTSUP); + + /* Mark all XSAVE components as in use. */ + ncomponents = flsl(xsave_mask); + for (i = 0; i < ncomponents; i++) { + if ((xsave_mask & (1ULL << i)) == 0) + continue; + area->xsave_header.xstate_bv |= 1ULL << i; + } + + 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; + + if (xsave_mask == 0) + return (ENOTSUP); + + /* + * Do not allow the header to be tampered with since it can cause the + * XSAVE ISA to #GP. + */ + if (memcmp(&area->xsave_header, &new_area->xsave_header, + sizeof(struct xsave_header)) != 0) + return (EINVAL); + + memcpy(area, new_area, fpu_save_len); + return (0); +} + #endif /* PTRACE */ diff --git sys/arch/amd64/include/ptrace.h sys/arch/amd64/include/ptrace.h index 27c0bf29c1db..812461f8e13e 100644 --- sys/arch/amd64/include/ptrace.h +++ sys/arch/amd64/include/ptrace.h @@ -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 sys/kern/sys_process.c sys/kern/sys_process.c index 6ab263c7eca3..1a72477fa7c8 100644 --- sys/kern/sys_process.c +++ sys/kern/sys_process.c @@ -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);