2019-06-03 07:44:50 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2012-03-05 11:49:33 +00:00
|
|
|
/*
|
|
|
|
* Based on arch/arm/kernel/ptrace.c
|
|
|
|
*
|
|
|
|
* By Ross Biro 1/23/92
|
|
|
|
* edited by Linus Torvalds
|
|
|
|
* ARM modifications Copyright (C) 2000 Russell King
|
|
|
|
* Copyright (C) 2012 ARM Ltd.
|
|
|
|
*/
|
|
|
|
|
2014-07-04 08:28:31 +01:00
|
|
|
#include <linux/audit.h>
|
2014-04-30 10:51:32 +01:00
|
|
|
#include <linux/compat.h>
|
2012-03-05 11:49:33 +00:00
|
|
|
#include <linux/kernel.h>
|
2017-02-08 18:51:30 +01:00
|
|
|
#include <linux/sched/signal.h>
|
2017-02-08 18:51:37 +01:00
|
|
|
#include <linux/sched/task_stack.h>
|
2012-03-05 11:49:33 +00:00
|
|
|
#include <linux/mm.h>
|
2018-04-25 17:13:40 +01:00
|
|
|
#include <linux/nospec.h>
|
2012-03-05 11:49:33 +00:00
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/ptrace.h>
|
|
|
|
#include <linux/user.h>
|
2014-11-28 05:26:39 +00:00
|
|
|
#include <linux/seccomp.h>
|
2012-03-05 11:49:33 +00:00
|
|
|
#include <linux/security.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/signal.h>
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
#include <linux/string.h>
|
2012-03-05 11:49:33 +00:00
|
|
|
#include <linux/uaccess.h>
|
|
|
|
#include <linux/perf_event.h>
|
|
|
|
#include <linux/hw_breakpoint.h>
|
|
|
|
#include <linux/regset.h>
|
|
|
|
#include <linux/elf.h>
|
2023-12-15 15:58:20 -05:00
|
|
|
#include <linux/rseq.h>
|
2012-03-05 11:49:33 +00:00
|
|
|
|
|
|
|
#include <asm/compat.h>
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
#include <asm/cpufeature.h>
|
2012-03-05 11:49:33 +00:00
|
|
|
#include <asm/debug-monitors.h>
|
2018-04-12 16:47:20 +01:00
|
|
|
#include <asm/fpsimd.h>
|
2024-10-01 23:59:06 +01:00
|
|
|
#include <asm/gcs.h>
|
2020-03-30 10:29:38 +01:00
|
|
|
#include <asm/mte.h>
|
2018-12-07 18:39:26 +00:00
|
|
|
#include <asm/pointer_auth.h>
|
2017-07-20 12:26:48 +01:00
|
|
|
#include <asm/stacktrace.h>
|
2014-07-04 08:28:31 +01:00
|
|
|
#include <asm/syscall.h>
|
2012-03-05 11:49:33 +00:00
|
|
|
#include <asm/traps.h>
|
|
|
|
#include <asm/system_misc.h>
|
|
|
|
|
2014-04-30 10:54:36 +01:00
|
|
|
#define CREATE_TRACE_POINTS
|
|
|
|
#include <trace/events/syscalls.h>
|
|
|
|
|
2016-07-08 12:35:45 -04:00
|
|
|
struct pt_regs_offset {
|
|
|
|
const char *name;
|
|
|
|
int offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
|
|
|
|
#define REG_OFFSET_END {.name = NULL, .offset = 0}
|
|
|
|
#define GPR_OFFSET_NAME(r) \
|
|
|
|
{.name = "x" #r, .offset = offsetof(struct pt_regs, regs[r])}
|
|
|
|
|
|
|
|
static const struct pt_regs_offset regoffset_table[] = {
|
|
|
|
GPR_OFFSET_NAME(0),
|
|
|
|
GPR_OFFSET_NAME(1),
|
|
|
|
GPR_OFFSET_NAME(2),
|
|
|
|
GPR_OFFSET_NAME(3),
|
|
|
|
GPR_OFFSET_NAME(4),
|
|
|
|
GPR_OFFSET_NAME(5),
|
|
|
|
GPR_OFFSET_NAME(6),
|
|
|
|
GPR_OFFSET_NAME(7),
|
|
|
|
GPR_OFFSET_NAME(8),
|
|
|
|
GPR_OFFSET_NAME(9),
|
|
|
|
GPR_OFFSET_NAME(10),
|
|
|
|
GPR_OFFSET_NAME(11),
|
|
|
|
GPR_OFFSET_NAME(12),
|
|
|
|
GPR_OFFSET_NAME(13),
|
|
|
|
GPR_OFFSET_NAME(14),
|
|
|
|
GPR_OFFSET_NAME(15),
|
|
|
|
GPR_OFFSET_NAME(16),
|
|
|
|
GPR_OFFSET_NAME(17),
|
|
|
|
GPR_OFFSET_NAME(18),
|
|
|
|
GPR_OFFSET_NAME(19),
|
|
|
|
GPR_OFFSET_NAME(20),
|
|
|
|
GPR_OFFSET_NAME(21),
|
|
|
|
GPR_OFFSET_NAME(22),
|
|
|
|
GPR_OFFSET_NAME(23),
|
|
|
|
GPR_OFFSET_NAME(24),
|
|
|
|
GPR_OFFSET_NAME(25),
|
|
|
|
GPR_OFFSET_NAME(26),
|
|
|
|
GPR_OFFSET_NAME(27),
|
|
|
|
GPR_OFFSET_NAME(28),
|
|
|
|
GPR_OFFSET_NAME(29),
|
|
|
|
GPR_OFFSET_NAME(30),
|
|
|
|
{.name = "lr", .offset = offsetof(struct pt_regs, regs[30])},
|
|
|
|
REG_OFFSET_NAME(sp),
|
|
|
|
REG_OFFSET_NAME(pc),
|
|
|
|
REG_OFFSET_NAME(pstate),
|
|
|
|
REG_OFFSET_END,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* regs_query_register_offset() - query register offset from its name
|
|
|
|
* @name: the name of a register
|
|
|
|
*
|
|
|
|
* regs_query_register_offset() returns the offset of a register in struct
|
|
|
|
* pt_regs from its name. If the name is invalid, this returns -EINVAL;
|
|
|
|
*/
|
|
|
|
int regs_query_register_offset(const char *name)
|
|
|
|
{
|
|
|
|
const struct pt_regs_offset *roff;
|
|
|
|
|
|
|
|
for (roff = regoffset_table; roff->name != NULL; roff++)
|
|
|
|
if (!strcmp(roff->name, name))
|
|
|
|
return roff->offset;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* regs_within_kernel_stack() - check the address in the stack
|
|
|
|
* @regs: pt_regs which contains kernel stack pointer.
|
|
|
|
* @addr: address which is checked.
|
|
|
|
*
|
|
|
|
* regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
|
|
|
|
* If @addr is within the kernel stack, it returns true. If not, returns false.
|
|
|
|
*/
|
|
|
|
static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
|
|
|
|
{
|
|
|
|
return ((addr & ~(THREAD_SIZE - 1)) ==
|
|
|
|
(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
|
2022-09-01 14:06:43 +01:00
|
|
|
on_irq_stack(addr, sizeof(unsigned long));
|
2016-07-08 12:35:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* regs_get_kernel_stack_nth() - get Nth entry of the stack
|
|
|
|
* @regs: pt_regs which contains kernel stack pointer.
|
|
|
|
* @n: stack entry number.
|
|
|
|
*
|
|
|
|
* regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
|
|
|
|
* is specified by @regs. If the @n th entry is NOT in the kernel stack,
|
|
|
|
* this returns 0.
|
|
|
|
*/
|
|
|
|
unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
|
|
|
|
{
|
|
|
|
unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
|
|
|
|
|
|
|
|
addr += n;
|
|
|
|
if (regs_within_kernel_stack(regs, (unsigned long)addr))
|
2025-06-04 00:55:33 +00:00
|
|
|
return READ_ONCE_NOCHECK(*addr);
|
2016-07-08 12:35:45 -04:00
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
/*
|
|
|
|
* TODO: does not yet catch signals sent when the child dies.
|
|
|
|
* in exit.c or in signal.c.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Called by kernel/ptrace.c when detaching..
|
|
|
|
*/
|
|
|
|
void ptrace_disable(struct task_struct *child)
|
|
|
|
{
|
2015-12-07 11:50:34 +00:00
|
|
|
/*
|
|
|
|
* This would be better off in core code, but PTRACE_DETACH has
|
|
|
|
* grown its fair share of arch-specific worts and changing it
|
|
|
|
* is likely to cause regressions on obscure architectures.
|
|
|
|
*/
|
|
|
|
user_disable_single_step(child);
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
|
|
|
/*
|
|
|
|
* Handle hitting a HW-breakpoint.
|
|
|
|
*/
|
|
|
|
static void ptrace_hbptriggered(struct perf_event *bp,
|
|
|
|
struct perf_sample_data *data,
|
|
|
|
struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
|
2018-09-22 10:47:54 +02:00
|
|
|
const char *desc = "Hardware breakpoint trap (ptrace)";
|
2012-03-05 11:49:33 +00:00
|
|
|
|
2018-01-22 14:50:53 -06:00
|
|
|
if (is_compat_task()) {
|
2018-01-22 14:37:25 -06:00
|
|
|
int si_errno = 0;
|
2018-01-22 14:50:53 -06:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARM_MAX_BRP; ++i) {
|
|
|
|
if (current->thread.debug.hbp_break[i] == bp) {
|
2018-01-22 14:37:25 -06:00
|
|
|
si_errno = (i << 1) + 1;
|
2018-01-22 14:50:53 -06:00
|
|
|
break;
|
|
|
|
}
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
2014-08-22 14:13:24 +01:00
|
|
|
|
2018-01-22 14:50:53 -06:00
|
|
|
for (i = 0; i < ARM_MAX_WRP; ++i) {
|
|
|
|
if (current->thread.debug.hbp_watch[i] == bp) {
|
2018-01-22 14:37:25 -06:00
|
|
|
si_errno = -((i << 1) + 1);
|
2018-01-22 14:50:53 -06:00
|
|
|
break;
|
|
|
|
}
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
2020-11-20 12:33:46 -08:00
|
|
|
arm64_force_sig_ptrace_errno_trap(si_errno, bkpt->trigger,
|
2018-09-22 10:52:41 +02:00
|
|
|
desc);
|
2021-02-01 19:21:09 -05:00
|
|
|
return;
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
2024-01-09 00:46:50 -03:00
|
|
|
|
2020-11-20 12:33:46 -08:00
|
|
|
arm64_force_sig_fault(SIGTRAP, TRAP_HWBKPT, bkpt->trigger, desc);
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unregister breakpoints from this task and reset the pointers in
|
|
|
|
* the thread_struct.
|
|
|
|
*/
|
|
|
|
void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct thread_struct *t = &tsk->thread;
|
|
|
|
|
|
|
|
for (i = 0; i < ARM_MAX_BRP; i++) {
|
|
|
|
if (t->debug.hbp_break[i]) {
|
|
|
|
unregister_hw_breakpoint(t->debug.hbp_break[i]);
|
|
|
|
t->debug.hbp_break[i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ARM_MAX_WRP; i++) {
|
|
|
|
if (t->debug.hbp_watch[i]) {
|
|
|
|
unregister_hw_breakpoint(t->debug.hbp_watch[i]);
|
|
|
|
t->debug.hbp_watch[i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ptrace_hw_copy_thread(struct task_struct *tsk)
|
|
|
|
{
|
|
|
|
memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct perf_event *ptrace_hbp_get_event(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
unsigned long idx)
|
|
|
|
{
|
|
|
|
struct perf_event *bp = ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
switch (note_type) {
|
|
|
|
case NT_ARM_HW_BREAK:
|
2018-04-25 17:13:40 +01:00
|
|
|
if (idx >= ARM_MAX_BRP)
|
|
|
|
goto out;
|
|
|
|
idx = array_index_nospec(idx, ARM_MAX_BRP);
|
|
|
|
bp = tsk->thread.debug.hbp_break[idx];
|
2012-03-05 11:49:33 +00:00
|
|
|
break;
|
|
|
|
case NT_ARM_HW_WATCH:
|
2018-04-25 17:13:40 +01:00
|
|
|
if (idx >= ARM_MAX_WRP)
|
|
|
|
goto out;
|
|
|
|
idx = array_index_nospec(idx, ARM_MAX_WRP);
|
|
|
|
bp = tsk->thread.debug.hbp_watch[idx];
|
2012-03-05 11:49:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-04-25 17:13:40 +01:00
|
|
|
out:
|
2012-03-05 11:49:33 +00:00
|
|
|
return bp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ptrace_hbp_set_event(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
unsigned long idx,
|
|
|
|
struct perf_event *bp)
|
|
|
|
{
|
|
|
|
int err = -EINVAL;
|
|
|
|
|
|
|
|
switch (note_type) {
|
|
|
|
case NT_ARM_HW_BREAK:
|
2018-07-10 19:01:22 +01:00
|
|
|
if (idx >= ARM_MAX_BRP)
|
|
|
|
goto out;
|
|
|
|
idx = array_index_nospec(idx, ARM_MAX_BRP);
|
|
|
|
tsk->thread.debug.hbp_break[idx] = bp;
|
|
|
|
err = 0;
|
2012-03-05 11:49:33 +00:00
|
|
|
break;
|
|
|
|
case NT_ARM_HW_WATCH:
|
2018-07-10 19:01:22 +01:00
|
|
|
if (idx >= ARM_MAX_WRP)
|
|
|
|
goto out;
|
|
|
|
idx = array_index_nospec(idx, ARM_MAX_WRP);
|
|
|
|
tsk->thread.debug.hbp_watch[idx] = bp;
|
|
|
|
err = 0;
|
2012-03-05 11:49:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-07-10 19:01:22 +01:00
|
|
|
out:
|
2012-03-05 11:49:33 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct perf_event *ptrace_hbp_create(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
unsigned long idx)
|
|
|
|
{
|
|
|
|
struct perf_event *bp;
|
|
|
|
struct perf_event_attr attr;
|
|
|
|
int err, type;
|
|
|
|
|
|
|
|
switch (note_type) {
|
|
|
|
case NT_ARM_HW_BREAK:
|
|
|
|
type = HW_BREAKPOINT_X;
|
|
|
|
break;
|
|
|
|
case NT_ARM_HW_WATCH:
|
|
|
|
type = HW_BREAKPOINT_RW;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ptrace_breakpoint_init(&attr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialise fields to sane defaults
|
|
|
|
* (i.e. values that will pass validation).
|
|
|
|
*/
|
|
|
|
attr.bp_addr = 0;
|
|
|
|
attr.bp_len = HW_BREAKPOINT_LEN_4;
|
|
|
|
attr.bp_type = type;
|
|
|
|
attr.disabled = 1;
|
|
|
|
|
|
|
|
bp = register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, tsk);
|
|
|
|
if (IS_ERR(bp))
|
|
|
|
return bp;
|
|
|
|
|
|
|
|
err = ptrace_hbp_set_event(note_type, tsk, idx, bp);
|
|
|
|
if (err)
|
|
|
|
return ERR_PTR(err);
|
|
|
|
|
|
|
|
return bp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
|
|
|
|
struct arch_hw_breakpoint_ctrl ctrl,
|
|
|
|
struct perf_event_attr *attr)
|
|
|
|
{
|
2016-11-14 19:32:43 +05:30
|
|
|
int err, len, type, offset, disabled = !ctrl.enabled;
|
2012-10-18 15:17:00 +01:00
|
|
|
|
2013-12-17 17:09:08 +00:00
|
|
|
attr->disabled = disabled;
|
|
|
|
if (disabled)
|
|
|
|
return 0;
|
|
|
|
|
2016-11-14 19:32:43 +05:30
|
|
|
err = arch_bp_generic_fields(ctrl, &len, &type, &offset);
|
2013-12-17 17:09:08 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
switch (note_type) {
|
|
|
|
case NT_ARM_HW_BREAK:
|
|
|
|
if ((type & HW_BREAKPOINT_X) != type)
|
2012-03-05 11:49:33 +00:00
|
|
|
return -EINVAL;
|
2013-12-17 17:09:08 +00:00
|
|
|
break;
|
|
|
|
case NT_ARM_HW_WATCH:
|
|
|
|
if ((type & HW_BREAKPOINT_RW) != type)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
attr->bp_len = len;
|
|
|
|
attr->bp_type = type;
|
2016-11-14 19:32:43 +05:30
|
|
|
attr->bp_addr += offset;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ptrace_hbp_get_resource_info(unsigned int note_type, u32 *info)
|
|
|
|
{
|
|
|
|
u8 num;
|
|
|
|
u32 reg = 0;
|
|
|
|
|
|
|
|
switch (note_type) {
|
|
|
|
case NT_ARM_HW_BREAK:
|
|
|
|
num = hw_breakpoint_slots(TYPE_INST);
|
|
|
|
break;
|
|
|
|
case NT_ARM_HW_WATCH:
|
|
|
|
num = hw_breakpoint_slots(TYPE_DATA);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
reg |= debug_monitors_arch();
|
|
|
|
reg <<= 8;
|
|
|
|
reg |= num;
|
|
|
|
|
|
|
|
*info = reg;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ptrace_hbp_get_ctrl(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
unsigned long idx,
|
|
|
|
u32 *ctrl)
|
|
|
|
{
|
|
|
|
struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
|
|
|
|
|
|
|
|
if (IS_ERR(bp))
|
|
|
|
return PTR_ERR(bp);
|
|
|
|
|
|
|
|
*ctrl = bp ? encode_ctrl_reg(counter_arch_bp(bp)->ctrl) : 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ptrace_hbp_get_addr(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
unsigned long idx,
|
|
|
|
u64 *addr)
|
|
|
|
{
|
|
|
|
struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
|
|
|
|
|
|
|
|
if (IS_ERR(bp))
|
|
|
|
return PTR_ERR(bp);
|
|
|
|
|
2016-11-14 19:32:43 +05:30
|
|
|
*addr = bp ? counter_arch_bp(bp)->address : 0;
|
2012-03-05 11:49:33 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct perf_event *ptrace_hbp_get_initialised_bp(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
unsigned long idx)
|
|
|
|
{
|
|
|
|
struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
|
|
|
|
|
|
|
|
if (!bp)
|
|
|
|
bp = ptrace_hbp_create(note_type, tsk, idx);
|
|
|
|
|
|
|
|
return bp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ptrace_hbp_set_ctrl(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
unsigned long idx,
|
|
|
|
u32 uctrl)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct perf_event *bp;
|
|
|
|
struct perf_event_attr attr;
|
|
|
|
struct arch_hw_breakpoint_ctrl ctrl;
|
|
|
|
|
|
|
|
bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);
|
|
|
|
if (IS_ERR(bp)) {
|
|
|
|
err = PTR_ERR(bp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = bp->attr;
|
|
|
|
decode_ctrl_reg(uctrl, &ctrl);
|
|
|
|
err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return modify_user_hw_breakpoint(bp, &attr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ptrace_hbp_set_addr(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
unsigned long idx,
|
|
|
|
u64 addr)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct perf_event *bp;
|
|
|
|
struct perf_event_attr attr;
|
|
|
|
|
|
|
|
bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);
|
|
|
|
if (IS_ERR(bp)) {
|
|
|
|
err = PTR_ERR(bp);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = bp->attr;
|
|
|
|
attr.bp_addr = addr;
|
|
|
|
err = modify_user_hw_breakpoint(bp, &attr);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PTRACE_HBP_ADDR_SZ sizeof(u64)
|
|
|
|
#define PTRACE_HBP_CTRL_SZ sizeof(u32)
|
2012-10-11 12:10:57 +01:00
|
|
|
#define PTRACE_HBP_PAD_SZ sizeof(u32)
|
2012-03-05 11:49:33 +00:00
|
|
|
|
|
|
|
static int hw_break_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2012-03-05 11:49:33 +00:00
|
|
|
{
|
|
|
|
unsigned int note_type = regset->core_note_type;
|
2020-05-27 19:09:44 -04:00
|
|
|
int ret, idx = 0;
|
2012-03-05 11:49:33 +00:00
|
|
|
u32 info, ctrl;
|
|
|
|
u64 addr;
|
|
|
|
|
|
|
|
/* Resource info */
|
|
|
|
ret = ptrace_hbp_get_resource_info(note_type, &info);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-05-27 19:09:44 -04:00
|
|
|
membuf_write(&to, &info, sizeof(info));
|
|
|
|
membuf_zero(&to, sizeof(u32));
|
2012-03-05 11:49:33 +00:00
|
|
|
/* (address, ctrl) registers */
|
2020-05-27 19:09:44 -04:00
|
|
|
while (to.left) {
|
2012-03-05 11:49:33 +00:00
|
|
|
ret = ptrace_hbp_get_addr(note_type, target, idx, &addr);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ptrace_hbp_get_ctrl(note_type, target, idx, &ctrl);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2020-05-27 19:09:44 -04:00
|
|
|
membuf_store(&to, addr);
|
|
|
|
membuf_store(&to, ctrl);
|
|
|
|
membuf_zero(&to, sizeof(u32));
|
2012-03-05 11:49:33 +00:00
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hw_break_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
unsigned int note_type = regset->core_note_type;
|
2012-10-11 12:10:57 +01:00
|
|
|
int ret, idx = 0, offset, limit;
|
2012-03-05 11:49:33 +00:00
|
|
|
u32 ctrl;
|
|
|
|
u64 addr;
|
|
|
|
|
2012-10-11 12:10:57 +01:00
|
|
|
/* Resource info and pad */
|
|
|
|
offset = offsetof(struct user_hwdebug_state, dbg_regs);
|
2022-10-15 00:22:25 +03:00
|
|
|
user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 0, offset);
|
2012-03-05 11:49:33 +00:00
|
|
|
|
|
|
|
/* (address, ctrl) registers */
|
|
|
|
limit = regset->n * regset->size;
|
|
|
|
while (count && offset < limit) {
|
2017-01-18 16:25:24 +00:00
|
|
|
if (count < PTRACE_HBP_ADDR_SZ)
|
|
|
|
return -EINVAL;
|
2012-03-05 11:49:33 +00:00
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr,
|
|
|
|
offset, offset + PTRACE_HBP_ADDR_SZ);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ptrace_hbp_set_addr(note_type, target, idx, addr);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
offset += PTRACE_HBP_ADDR_SZ;
|
|
|
|
|
2017-01-18 16:25:24 +00:00
|
|
|
if (!count)
|
|
|
|
break;
|
2012-03-05 11:49:33 +00:00
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl,
|
|
|
|
offset, offset + PTRACE_HBP_CTRL_SZ);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = ptrace_hbp_set_ctrl(note_type, target, idx, ctrl);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
offset += PTRACE_HBP_CTRL_SZ;
|
2012-10-11 12:10:57 +01:00
|
|
|
|
2022-10-15 00:22:25 +03:00
|
|
|
user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
|
|
|
|
offset, offset + PTRACE_HBP_PAD_SZ);
|
2012-10-11 12:10:57 +01:00
|
|
|
offset += PTRACE_HBP_PAD_SZ;
|
2012-03-05 11:49:33 +00:00
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
|
|
|
|
|
|
|
|
static int gpr_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2012-03-05 11:49:33 +00:00
|
|
|
{
|
|
|
|
struct user_pt_regs *uregs = &task_pt_regs(target)->user_regs;
|
2020-05-27 19:09:44 -04:00
|
|
|
return membuf_write(&to, uregs, sizeof(*uregs));
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gpr_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
2017-01-18 16:25:20 +00:00
|
|
|
struct user_pt_regs newregs = task_pt_regs(target)->user_regs;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, 0, -1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-03-01 14:18:50 +00:00
|
|
|
if (!valid_user_regs(&newregs, target))
|
2012-03-05 11:49:33 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
task_pt_regs(target)->user_regs = newregs;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-13 23:30:21 +00:00
|
|
|
static int fpr_active(struct task_struct *target, const struct user_regset *regset)
|
|
|
|
{
|
|
|
|
if (!system_supports_fpsimd())
|
|
|
|
return -ENODEV;
|
|
|
|
return regset->n;
|
|
|
|
}
|
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
/*
|
|
|
|
* TODO: update fp accessors for lazy context switching (sync/flush hwstate)
|
|
|
|
*/
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
static int __fpr_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2012-03-05 11:49:33 +00:00
|
|
|
{
|
|
|
|
struct user_fpsimd_state *uregs;
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
arm64/fpsimd: Clarify sve_sync_*() functions
The sve_sync_{to,from}_fpsimd*() functions are intended to
extract/insert the currently effective FPSIMD state of a task regardless
of whether the task's state is saved in FPSIMD format or SVE format.
Historically they were only used by ptrace, but sve_sync_to_fpsimd() is
now used more widely, and sve_sync_from_fpsimd_zeropad() may be used
more widely in future.
When FPSIMD/SVE state tracking was changed across commits:
baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
a0136be443d5 (arm64/fpsimd: Load FP state based on recorded data type")
bbc6172eefdb ("arm64/fpsimd: SME no longer requires SVE register state")
8c845e273104 ("arm64/sve: Leave SVE enabled on syscall if we don't context switch")
... sve_sync_to_fpsimd() was updated to consider task->thread.fp_type
rather than the task's TIF_SVE and PSTATE.SM, but (apparently due to an
oversight) sve_sync_from_fpsimd_zeropad() was left as-is, leaving the
two inconsistent.
Due to this, sve_sync_from_fpsimd_zeropad() may copy state from
task->thread.uw.fpsimd_state into task->thread.sve_state when
task->thread.fp_type == FP_STATE_FPSIMD. This is redundant (but benign)
as task->thread.uw.fpsimd_state is the effective state that will be
restored, and task->thread.sve_state will not be consumed. For
consistency, and to avoid the redundant work, it better for
sve_sync_from_fpsimd_zeropad() to consider task->thread.fp_type alone,
matching sve_sync_to_fpsimd().
The naming of both functions is somehat unfortunate, as it is unclear
when and why they copy state. It would be better to describe them in
terms of the effective state.
Considering all of the above, clean this up:
* Adjust sve_sync_from_fpsimd_zeropad() to consider
task->thread.fp_type.
* Update comments to clarify the intended semantics/usage. I've removed
the description that task->thread.sve_state must have been allocated,
as this is only necessary when task->thread.fp_type == FP_STATE_SVE,
which itself implies that task->thread.sve_state must have been
allocated.
* Rename the functions to more clearly indicate when/why they copy
state:
- sve_sync_to_fpsimd() => fpsimd_sync_from_effective_state()
- sve_sync_from_fpsimd_zeropad => fpsimd_sync_to_effective_state_zeropad()
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-7-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:26 +01:00
|
|
|
fpsimd_sync_from_effective_state(target);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2018-03-28 10:50:49 +01:00
|
|
|
uregs = &target->thread.uw.fpsimd_state;
|
2017-06-21 16:00:43 +01:00
|
|
|
|
2020-05-27 19:09:44 -04:00
|
|
|
return membuf_write(&to, uregs, sizeof(*uregs));
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fpr_get(struct task_struct *target, const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
{
|
2020-01-13 23:30:21 +00:00
|
|
|
if (!system_supports_fpsimd())
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-06-21 16:00:43 +01:00
|
|
|
if (target == current)
|
|
|
|
fpsimd_preserve_current_state();
|
|
|
|
|
2020-05-27 19:09:44 -04:00
|
|
|
return __fpr_get(target, regset, to);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __fpr_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf,
|
|
|
|
unsigned int start_pos)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct user_fpsimd_state newstate;
|
|
|
|
|
|
|
|
/*
|
2018-03-28 10:50:49 +01:00
|
|
|
* Ensure target->thread.uw.fpsimd_state is up to date, so that a
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
* short copyin can't resurrect stale data.
|
|
|
|
*/
|
arm64/fpsimd: Clarify sve_sync_*() functions
The sve_sync_{to,from}_fpsimd*() functions are intended to
extract/insert the currently effective FPSIMD state of a task regardless
of whether the task's state is saved in FPSIMD format or SVE format.
Historically they were only used by ptrace, but sve_sync_to_fpsimd() is
now used more widely, and sve_sync_from_fpsimd_zeropad() may be used
more widely in future.
When FPSIMD/SVE state tracking was changed across commits:
baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
a0136be443d5 (arm64/fpsimd: Load FP state based on recorded data type")
bbc6172eefdb ("arm64/fpsimd: SME no longer requires SVE register state")
8c845e273104 ("arm64/sve: Leave SVE enabled on syscall if we don't context switch")
... sve_sync_to_fpsimd() was updated to consider task->thread.fp_type
rather than the task's TIF_SVE and PSTATE.SM, but (apparently due to an
oversight) sve_sync_from_fpsimd_zeropad() was left as-is, leaving the
two inconsistent.
Due to this, sve_sync_from_fpsimd_zeropad() may copy state from
task->thread.uw.fpsimd_state into task->thread.sve_state when
task->thread.fp_type == FP_STATE_FPSIMD. This is redundant (but benign)
as task->thread.uw.fpsimd_state is the effective state that will be
restored, and task->thread.sve_state will not be consumed. For
consistency, and to avoid the redundant work, it better for
sve_sync_from_fpsimd_zeropad() to consider task->thread.fp_type alone,
matching sve_sync_to_fpsimd().
The naming of both functions is somehat unfortunate, as it is unclear
when and why they copy state. It would be better to describe them in
terms of the effective state.
Considering all of the above, clean this up:
* Adjust sve_sync_from_fpsimd_zeropad() to consider
task->thread.fp_type.
* Update comments to clarify the intended semantics/usage. I've removed
the description that task->thread.sve_state must have been allocated,
as this is only necessary when task->thread.fp_type == FP_STATE_SVE,
which itself implies that task->thread.sve_state must have been
allocated.
* Rename the functions to more clearly indicate when/why they copy
state:
- sve_sync_to_fpsimd() => fpsimd_sync_from_effective_state()
- sve_sync_from_fpsimd_zeropad => fpsimd_sync_to_effective_state_zeropad()
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-7-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:26 +01:00
|
|
|
fpsimd_sync_from_effective_state(target);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2018-03-28 10:50:49 +01:00
|
|
|
newstate = target->thread.uw.fpsimd_state;
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate,
|
|
|
|
start_pos, start_pos + sizeof(newstate));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2018-03-28 10:50:49 +01:00
|
|
|
target->thread.uw.fpsimd_state = newstate;
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
|
|
|
return ret;
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fpr_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2020-01-13 23:30:21 +00:00
|
|
|
if (!system_supports_fpsimd())
|
|
|
|
return -EINVAL;
|
|
|
|
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
ret = __fpr_set(target, regset, pos, count, kbuf, ubuf, 0);
|
2012-03-05 11:49:33 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
arm64/fpsimd: Clarify sve_sync_*() functions
The sve_sync_{to,from}_fpsimd*() functions are intended to
extract/insert the currently effective FPSIMD state of a task regardless
of whether the task's state is saved in FPSIMD format or SVE format.
Historically they were only used by ptrace, but sve_sync_to_fpsimd() is
now used more widely, and sve_sync_from_fpsimd_zeropad() may be used
more widely in future.
When FPSIMD/SVE state tracking was changed across commits:
baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
a0136be443d5 (arm64/fpsimd: Load FP state based on recorded data type")
bbc6172eefdb ("arm64/fpsimd: SME no longer requires SVE register state")
8c845e273104 ("arm64/sve: Leave SVE enabled on syscall if we don't context switch")
... sve_sync_to_fpsimd() was updated to consider task->thread.fp_type
rather than the task's TIF_SVE and PSTATE.SM, but (apparently due to an
oversight) sve_sync_from_fpsimd_zeropad() was left as-is, leaving the
two inconsistent.
Due to this, sve_sync_from_fpsimd_zeropad() may copy state from
task->thread.uw.fpsimd_state into task->thread.sve_state when
task->thread.fp_type == FP_STATE_FPSIMD. This is redundant (but benign)
as task->thread.uw.fpsimd_state is the effective state that will be
restored, and task->thread.sve_state will not be consumed. For
consistency, and to avoid the redundant work, it better for
sve_sync_from_fpsimd_zeropad() to consider task->thread.fp_type alone,
matching sve_sync_to_fpsimd().
The naming of both functions is somehat unfortunate, as it is unclear
when and why they copy state. It would be better to describe them in
terms of the effective state.
Considering all of the above, clean this up:
* Adjust sve_sync_from_fpsimd_zeropad() to consider
task->thread.fp_type.
* Update comments to clarify the intended semantics/usage. I've removed
the description that task->thread.sve_state must have been allocated,
as this is only necessary when task->thread.fp_type == FP_STATE_SVE,
which itself implies that task->thread.sve_state must have been
allocated.
* Rename the functions to more clearly indicate when/why they copy
state:
- sve_sync_to_fpsimd() => fpsimd_sync_from_effective_state()
- sve_sync_from_fpsimd_zeropad => fpsimd_sync_to_effective_state_zeropad()
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-7-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:26 +01:00
|
|
|
fpsimd_sync_to_effective_state_zeropad(target);
|
2014-05-08 11:20:23 +02:00
|
|
|
fpsimd_flush_task_state(target);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tls_get(struct task_struct *target, const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2012-03-05 11:49:33 +00:00
|
|
|
{
|
2022-08-29 16:49:20 +01:00
|
|
|
int ret;
|
|
|
|
|
2017-06-21 16:00:44 +01:00
|
|
|
if (target == current)
|
|
|
|
tls_preserve_current_state();
|
|
|
|
|
2022-08-29 16:49:20 +01:00
|
|
|
ret = membuf_store(&to, target->thread.uw.tp_value);
|
|
|
|
if (system_supports_tpidr2())
|
|
|
|
ret = membuf_store(&to, target->thread.tpidr2_el0);
|
|
|
|
else
|
|
|
|
ret = membuf_zero(&to, sizeof(u64));
|
|
|
|
|
|
|
|
return ret;
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tls_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
2022-08-29 16:49:20 +01:00
|
|
|
unsigned long tls[2];
|
|
|
|
|
|
|
|
tls[0] = target->thread.uw.tp_value;
|
2022-12-27 13:01:08 +00:00
|
|
|
if (system_supports_tpidr2())
|
2022-08-29 16:49:20 +01:00
|
|
|
tls[1] = target->thread.tpidr2_el0;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
2022-08-29 16:49:20 +01:00
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, tls, 0, count);
|
2012-03-05 11:49:33 +00:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2022-08-29 16:49:20 +01:00
|
|
|
target->thread.uw.tp_value = tls[0];
|
2022-12-27 13:01:08 +00:00
|
|
|
if (system_supports_tpidr2())
|
2022-08-29 16:49:20 +01:00
|
|
|
target->thread.tpidr2_el0 = tls[1];
|
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-03-06 23:14:50 +00:00
|
|
|
static int fpmr_get(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
struct membuf to)
|
|
|
|
{
|
|
|
|
if (!system_supports_fpmr())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (target == current)
|
|
|
|
fpsimd_preserve_current_state();
|
|
|
|
|
|
|
|
return membuf_store(&to, target->thread.uw.fpmr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fpmr_set(struct task_struct *target, const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
unsigned long fpmr;
|
|
|
|
|
|
|
|
if (!system_supports_fpmr())
|
|
|
|
return -EINVAL;
|
|
|
|
|
arm64: ptrace: fix partial SETREGSET for NT_ARM_FPMR
Currently fpmr_set() doesn't initialize the temporary 'fpmr' variable,
and a SETREGSET call with a length of zero will leave this
uninitialized. Consequently an arbitrary value will be written back to
target->thread.uw.fpmr, potentially leaking up to 64 bits of memory from
the kernel stack. The read is limited to a specific slot on the stack,
and the issue does not provide a write mechanism.
Fix this by initializing the temporary value before copying the regset
from userspace, as for other regsets (e.g. NT_PRSTATUS, NT_PRFPREG,
NT_ARM_SYSTEM_CALL). In the case of a zero-length write, the existing
contents of FPMR will be retained.
Before this patch:
| # ./fpmr-test
| Attempting to write NT_ARM_FPMR::fpmr = 0x900d900d900d900d
| SETREGSET(nt=0x40e, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_FPMR::fpmr
| GETREGSET(nt=0x40e, len=8) read 8 bytes
| Read NT_ARM_FPMR::fpmr = 0x900d900d900d900d
|
| Attempting to write NT_ARM_FPMR (zero length)
| SETREGSET(nt=0x40e, len=0) wrote 0 bytes
|
| Attempting to read NT_ARM_FPMR::fpmr
| GETREGSET(nt=0x40e, len=8) read 8 bytes
| Read NT_ARM_FPMR::fpmr = 0xffff800083963d50
After this patch:
| # ./fpmr-test
| Attempting to write NT_ARM_FPMR::fpmr = 0x900d900d900d900d
| SETREGSET(nt=0x40e, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_FPMR::fpmr
| GETREGSET(nt=0x40e, len=8) read 8 bytes
| Read NT_ARM_FPMR::fpmr = 0x900d900d900d900d
|
| Attempting to write NT_ARM_FPMR (zero length)
| SETREGSET(nt=0x40e, len=0) wrote 0 bytes
|
| Attempting to read NT_ARM_FPMR::fpmr
| GETREGSET(nt=0x40e, len=8) read 8 bytes
| Read NT_ARM_FPMR::fpmr = 0x900d900d900d900d
Fixes: 4035c22ef7d4 ("arm64/ptrace: Expose FPMR via ptrace")
Cc: <stable@vger.kernel.org> # 6.9.x
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241205121655.1824269-3-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2024-12-05 12:16:53 +00:00
|
|
|
fpmr = target->thread.uw.fpmr;
|
|
|
|
|
2024-03-06 23:14:50 +00:00
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpmr, 0, count);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
target->thread.uw.fpmr = fpmr;
|
|
|
|
|
|
|
|
fpsimd_flush_task_state(target);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-28 05:26:34 +00:00
|
|
|
static int system_call_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2014-11-28 05:26:34 +00:00
|
|
|
{
|
2020-05-27 19:09:44 -04:00
|
|
|
return membuf_store(&to, task_pt_regs(target)->syscallno);
|
2014-11-28 05:26:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int system_call_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
2017-01-18 16:25:21 +00:00
|
|
|
int syscallno = task_pt_regs(target)->syscallno;
|
|
|
|
int ret;
|
2014-11-28 05:26:34 +00:00
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &syscallno, 0, -1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
task_pt_regs(target)->syscallno = syscallno;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
#ifdef CONFIG_ARM64_SVE
|
|
|
|
|
|
|
|
static void sve_init_header_from_task(struct user_sve_header *header,
|
2022-04-19 12:22:28 +01:00
|
|
|
struct task_struct *target,
|
|
|
|
enum vec_type type)
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
{
|
|
|
|
unsigned int vq;
|
2022-04-19 12:22:28 +01:00
|
|
|
bool active;
|
|
|
|
enum vec_type task_type;
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
|
|
|
memset(header, 0, sizeof(*header));
|
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
/* Check if the requested registers are active for the task */
|
|
|
|
if (thread_sm_enabled(&target->thread))
|
|
|
|
task_type = ARM64_VEC_SME;
|
|
|
|
else
|
|
|
|
task_type = ARM64_VEC_SVE;
|
|
|
|
active = (task_type == type);
|
|
|
|
|
arm64/fpsimd: ptrace: Do not present register data for inactive mode
The SME ptrace ABI is written around the incorrect assumption that
SVE_PT_REGS_FPSIMD and SVE_PT_REGS_SVE are independent bit flags, where
it is possible for both to be clear. In reality they are different
values for bit 0 of the header flags, where SVE_PT_REGS_FPSIMD is 0 and
SVE_PT_REGS_SVE is 1. In cases where code was written expecting that
neither bit flag would be set, the value is equivalent to
SVE_PT_REGS_FPSIMD.
One consequence of this is that reads of the NT_ARM_SVE or NT_ARM_SSVE
will erroneously present data from the other mode:
* When PSTATE.SM==1, reads of NT_ARM_SVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from streaming mode.
* When PSTATE.SM==0, reads of NT_ARM_SSVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from non-streaming mode.
The original intent was that no register data would be provided in these
cases, as described in commit:
e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Luckily, debuggers do not consume the bogus register data. Both GDB and
LLDB read the NT_ARM_SSVE regset before the NT_ARM_SVE regset, and
assume that when the NT_ARM_SSVE header presents SVE_PT_REGS_FPSIMD, it
is necessary to read register contents from the NT_ARM_SVE regset,
regardless of whether the NT_ARM_SSVE regset provided bogus register
data.
Fix the code to stop presenting register data from the inactive mode.
At the same time, make the manipulation of the flag clearer, and remove
the bogus comment from sve_set_common(). I've given this a quick spin
with GDB and LLDB, and both seem happy.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-18-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:37 +01:00
|
|
|
if (active && target->thread.fp_type == FP_STATE_SVE)
|
|
|
|
header->flags = SVE_PT_REGS_SVE;
|
|
|
|
else
|
|
|
|
header->flags = SVE_PT_REGS_FPSIMD;
|
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
switch (type) {
|
|
|
|
case ARM64_VEC_SVE:
|
|
|
|
if (test_tsk_thread_flag(target, TIF_SVE_VL_INHERIT))
|
|
|
|
header->flags |= SVE_PT_VL_INHERIT;
|
|
|
|
break;
|
|
|
|
case ARM64_VEC_SME:
|
|
|
|
if (test_tsk_thread_flag(target, TIF_SME_VL_INHERIT))
|
|
|
|
header->flags |= SVE_PT_VL_INHERIT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
return;
|
|
|
|
}
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
header->vl = task_get_vl(target, type);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
vq = sve_vq_from_vl(header->vl);
|
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
header->max_vl = vec_max_vl(type);
|
arm64/fpsimd: ptrace: Do not present register data for inactive mode
The SME ptrace ABI is written around the incorrect assumption that
SVE_PT_REGS_FPSIMD and SVE_PT_REGS_SVE are independent bit flags, where
it is possible for both to be clear. In reality they are different
values for bit 0 of the header flags, where SVE_PT_REGS_FPSIMD is 0 and
SVE_PT_REGS_SVE is 1. In cases where code was written expecting that
neither bit flag would be set, the value is equivalent to
SVE_PT_REGS_FPSIMD.
One consequence of this is that reads of the NT_ARM_SVE or NT_ARM_SSVE
will erroneously present data from the other mode:
* When PSTATE.SM==1, reads of NT_ARM_SVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from streaming mode.
* When PSTATE.SM==0, reads of NT_ARM_SSVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from non-streaming mode.
The original intent was that no register data would be provided in these
cases, as described in commit:
e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Luckily, debuggers do not consume the bogus register data. Both GDB and
LLDB read the NT_ARM_SSVE regset before the NT_ARM_SVE regset, and
assume that when the NT_ARM_SSVE header presents SVE_PT_REGS_FPSIMD, it
is necessary to read register contents from the NT_ARM_SVE regset,
regardless of whether the NT_ARM_SSVE regset provided bogus register
data.
Fix the code to stop presenting register data from the inactive mode.
At the same time, make the manipulation of the flag clearer, and remove
the bogus comment from sve_set_common(). I've given this a quick spin
with GDB and LLDB, and both seem happy.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-18-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:37 +01:00
|
|
|
if (active)
|
|
|
|
header->size = SVE_PT_SIZE(vq, header->flags);
|
|
|
|
else
|
|
|
|
header->size = sizeof(header);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
header->max_size = SVE_PT_SIZE(sve_vq_from_vl(header->max_vl),
|
|
|
|
SVE_PT_REGS_SVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int sve_size_from_header(struct user_sve_header const *header)
|
|
|
|
{
|
|
|
|
return ALIGN(header->size, SVE_VQ_BYTES);
|
|
|
|
}
|
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
static int sve_get_common(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
struct membuf to,
|
|
|
|
enum vec_type type)
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
{
|
|
|
|
struct user_sve_header header;
|
|
|
|
unsigned int vq;
|
|
|
|
unsigned long start, end;
|
|
|
|
|
2025-05-08 14:26:36 +01:00
|
|
|
if (target == current)
|
|
|
|
fpsimd_preserve_current_state();
|
|
|
|
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
/* Header */
|
2022-04-19 12:22:28 +01:00
|
|
|
sve_init_header_from_task(&header, target, type);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
vq = sve_vq_from_vl(header.vl);
|
|
|
|
|
2020-05-27 19:09:44 -04:00
|
|
|
membuf_write(&to, &header, sizeof(header));
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
|
|
|
BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header));
|
2022-04-19 12:22:28 +01:00
|
|
|
BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header));
|
|
|
|
|
arm64/fpsimd: ptrace: Do not present register data for inactive mode
The SME ptrace ABI is written around the incorrect assumption that
SVE_PT_REGS_FPSIMD and SVE_PT_REGS_SVE are independent bit flags, where
it is possible for both to be clear. In reality they are different
values for bit 0 of the header flags, where SVE_PT_REGS_FPSIMD is 0 and
SVE_PT_REGS_SVE is 1. In cases where code was written expecting that
neither bit flag would be set, the value is equivalent to
SVE_PT_REGS_FPSIMD.
One consequence of this is that reads of the NT_ARM_SVE or NT_ARM_SSVE
will erroneously present data from the other mode:
* When PSTATE.SM==1, reads of NT_ARM_SVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from streaming mode.
* When PSTATE.SM==0, reads of NT_ARM_SSVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from non-streaming mode.
The original intent was that no register data would be provided in these
cases, as described in commit:
e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Luckily, debuggers do not consume the bogus register data. Both GDB and
LLDB read the NT_ARM_SSVE regset before the NT_ARM_SVE regset, and
assume that when the NT_ARM_SSVE header presents SVE_PT_REGS_FPSIMD, it
is necessary to read register contents from the NT_ARM_SVE regset,
regardless of whether the NT_ARM_SSVE regset provided bogus register
data.
Fix the code to stop presenting register data from the inactive mode.
At the same time, make the manipulation of the flag clearer, and remove
the bogus comment from sve_set_common(). I've given this a quick spin
with GDB and LLDB, and both seem happy.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-18-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:37 +01:00
|
|
|
/*
|
|
|
|
* When the requested vector type is not active, do not present data
|
|
|
|
* from the other mode to userspace.
|
|
|
|
*/
|
|
|
|
if (header.size == sizeof(header))
|
|
|
|
return 0;
|
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
switch ((header.flags & SVE_PT_REGS_MASK)) {
|
|
|
|
case SVE_PT_REGS_FPSIMD:
|
2020-05-27 19:09:44 -04:00
|
|
|
return __fpr_get(target, regset, to);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
case SVE_PT_REGS_SVE:
|
|
|
|
start = SVE_PT_SVE_OFFSET;
|
|
|
|
end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq);
|
|
|
|
membuf_write(&to, target->thread.sve_state, end - start);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
start = end;
|
|
|
|
end = SVE_PT_SVE_FPSR_OFFSET(vq);
|
|
|
|
membuf_zero(&to, end - start);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
/*
|
|
|
|
* Copy fpsr, and fpcr which must follow contiguously in
|
|
|
|
* struct fpsimd_state:
|
|
|
|
*/
|
|
|
|
start = end;
|
|
|
|
end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE;
|
|
|
|
membuf_write(&to, &target->thread.uw.fpsimd_state.fpsr,
|
|
|
|
end - start);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
start = end;
|
|
|
|
end = sve_size_from_header(&header);
|
|
|
|
return membuf_zero(&to, end - start);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
default:
|
arm64/fpsimd: ptrace: Do not present register data for inactive mode
The SME ptrace ABI is written around the incorrect assumption that
SVE_PT_REGS_FPSIMD and SVE_PT_REGS_SVE are independent bit flags, where
it is possible for both to be clear. In reality they are different
values for bit 0 of the header flags, where SVE_PT_REGS_FPSIMD is 0 and
SVE_PT_REGS_SVE is 1. In cases where code was written expecting that
neither bit flag would be set, the value is equivalent to
SVE_PT_REGS_FPSIMD.
One consequence of this is that reads of the NT_ARM_SVE or NT_ARM_SSVE
will erroneously present data from the other mode:
* When PSTATE.SM==1, reads of NT_ARM_SVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from streaming mode.
* When PSTATE.SM==0, reads of NT_ARM_SSVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from non-streaming mode.
The original intent was that no register data would be provided in these
cases, as described in commit:
e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Luckily, debuggers do not consume the bogus register data. Both GDB and
LLDB read the NT_ARM_SSVE regset before the NT_ARM_SVE regset, and
assume that when the NT_ARM_SSVE header presents SVE_PT_REGS_FPSIMD, it
is necessary to read register contents from the NT_ARM_SVE regset,
regardless of whether the NT_ARM_SSVE regset provided bogus register
data.
Fix the code to stop presenting register data from the inactive mode.
At the same time, make the manipulation of the flag clearer, and remove
the bogus comment from sve_set_common(). I've given this a quick spin
with GDB and LLDB, and both seem happy.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-18-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:37 +01:00
|
|
|
BUILD_BUG();
|
2022-04-19 12:22:28 +01:00
|
|
|
}
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
}
|
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
static int sve_get(struct task_struct *target,
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
const struct user_regset *regset,
|
2022-04-19 12:22:28 +01:00
|
|
|
struct membuf to)
|
|
|
|
{
|
|
|
|
if (!system_supports_sve())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return sve_get_common(target, regset, to, ARM64_VEC_SVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sve_set_common(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf,
|
|
|
|
enum vec_type type)
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct user_sve_header header;
|
|
|
|
unsigned int vq;
|
|
|
|
unsigned long start, end;
|
2025-05-08 14:26:38 +01:00
|
|
|
bool fpsimd;
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
arm64/fpsimd: ptrace: Gracefully handle errors
Within sve_set_common() we do not handle error conditions correctly:
* When writing to NT_ARM_SSVE, if sme_alloc() fails, the task will be
left with task->thread.sme_state==NULL, but TIF_SME will be set and
task->thread.fp_type==FP_STATE_SVE. This will result in a subsequent
null pointer dereference when the task's state is loaded or otherwise
manipulated.
* When writing to NT_ARM_SSVE, if sve_alloc() fails, the task will be
left with task->thread.sve_state==NULL, but TIF_SME will be set,
PSTATE.SM will be set, and task->thread.fp_type==FP_STATE_FPSIMD.
This is not a legitimate state, and can result in various problems,
including a subsequent null pointer dereference and/or the task
inheriting stale streaming mode register state the next time its state
is loaded into hardware.
* When writing to NT_ARM_SSVE, if the VL is changed but the resulting VL
differs from that in the header, the task will be left with TIF_SME
set, PSTATE.SM set, but task->thread.fp_type==FP_STATE_FPSIMD. This is
not a legitimate state, and can result in various problems as
described above.
Avoid these problems by allocating memory earlier, and by changing the
task's saved fp_type to FP_STATE_SVE before skipping register writes due
to a change of VL.
To make early returns simpler, I've moved the call to
fpsimd_flush_task_state() earlier. As the tracee's state has already
been saved, and the tracee is known to be blocked for the duration of
sve_set_common(), it doesn't matter whether this is called at the start
or the end.
For consistency I've moved the setting of TIF_SVE earlier. This will be
cleared when loading FPSIMD-only state, and so moving this has no
resulting functional change.
Note that we only allocate the memory for SVE state when SVE register
contents are provided, avoiding unnecessary memory allocations for tasks
which only use FPSIMD.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Fixes: baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 5d0a8d2fba50 ("arm64/ptrace: Ensure that SME is set up for target when writing SSVE state")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-20-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:39 +01:00
|
|
|
fpsimd_flush_task_state(target);
|
|
|
|
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
/* Header */
|
|
|
|
if (count < sizeof(header))
|
|
|
|
return -EINVAL;
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &header,
|
|
|
|
0, sizeof(header));
|
|
|
|
if (ret)
|
arm64/fpsimd: ptrace: Gracefully handle errors
Within sve_set_common() we do not handle error conditions correctly:
* When writing to NT_ARM_SSVE, if sme_alloc() fails, the task will be
left with task->thread.sme_state==NULL, but TIF_SME will be set and
task->thread.fp_type==FP_STATE_SVE. This will result in a subsequent
null pointer dereference when the task's state is loaded or otherwise
manipulated.
* When writing to NT_ARM_SSVE, if sve_alloc() fails, the task will be
left with task->thread.sve_state==NULL, but TIF_SME will be set,
PSTATE.SM will be set, and task->thread.fp_type==FP_STATE_FPSIMD.
This is not a legitimate state, and can result in various problems,
including a subsequent null pointer dereference and/or the task
inheriting stale streaming mode register state the next time its state
is loaded into hardware.
* When writing to NT_ARM_SSVE, if the VL is changed but the resulting VL
differs from that in the header, the task will be left with TIF_SME
set, PSTATE.SM set, but task->thread.fp_type==FP_STATE_FPSIMD. This is
not a legitimate state, and can result in various problems as
described above.
Avoid these problems by allocating memory earlier, and by changing the
task's saved fp_type to FP_STATE_SVE before skipping register writes due
to a change of VL.
To make early returns simpler, I've moved the call to
fpsimd_flush_task_state() earlier. As the tracee's state has already
been saved, and the tracee is known to be blocked for the duration of
sve_set_common(), it doesn't matter whether this is called at the start
or the end.
For consistency I've moved the setting of TIF_SVE earlier. This will be
cleared when loading FPSIMD-only state, and so moving this has no
resulting functional change.
Note that we only allocate the memory for SVE state when SVE register
contents are provided, avoiding unnecessary memory allocations for tasks
which only use FPSIMD.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Fixes: baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 5d0a8d2fba50 ("arm64/ptrace: Ensure that SME is set up for target when writing SSVE state")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-20-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:39 +01:00
|
|
|
return ret;
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2025-05-08 14:26:38 +01:00
|
|
|
/*
|
|
|
|
* Streaming SVE data is always stored and presented in SVE format.
|
|
|
|
* Require the user to provide SVE formatted data for consistency, and
|
|
|
|
* to avoid the risk that we configure the task into an invalid state.
|
|
|
|
*/
|
|
|
|
fpsimd = (header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD;
|
|
|
|
if (fpsimd && type == ARM64_VEC_SME)
|
|
|
|
return -EINVAL;
|
|
|
|
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
/*
|
2019-08-07 11:34:45 +01:00
|
|
|
* Apart from SVE_PT_REGS_MASK, all SVE_PT_* flags are consumed by
|
2021-12-10 18:40:58 +00:00
|
|
|
* vec_set_vector_length(), which will also validate them for us:
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
*/
|
2022-04-19 12:22:28 +01:00
|
|
|
ret = vec_set_vector_length(target, type, header.vl,
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
((unsigned long)header.flags & ~SVE_PT_REGS_MASK) << 16);
|
|
|
|
if (ret)
|
arm64/fpsimd: ptrace: Gracefully handle errors
Within sve_set_common() we do not handle error conditions correctly:
* When writing to NT_ARM_SSVE, if sme_alloc() fails, the task will be
left with task->thread.sme_state==NULL, but TIF_SME will be set and
task->thread.fp_type==FP_STATE_SVE. This will result in a subsequent
null pointer dereference when the task's state is loaded or otherwise
manipulated.
* When writing to NT_ARM_SSVE, if sve_alloc() fails, the task will be
left with task->thread.sve_state==NULL, but TIF_SME will be set,
PSTATE.SM will be set, and task->thread.fp_type==FP_STATE_FPSIMD.
This is not a legitimate state, and can result in various problems,
including a subsequent null pointer dereference and/or the task
inheriting stale streaming mode register state the next time its state
is loaded into hardware.
* When writing to NT_ARM_SSVE, if the VL is changed but the resulting VL
differs from that in the header, the task will be left with TIF_SME
set, PSTATE.SM set, but task->thread.fp_type==FP_STATE_FPSIMD. This is
not a legitimate state, and can result in various problems as
described above.
Avoid these problems by allocating memory earlier, and by changing the
task's saved fp_type to FP_STATE_SVE before skipping register writes due
to a change of VL.
To make early returns simpler, I've moved the call to
fpsimd_flush_task_state() earlier. As the tracee's state has already
been saved, and the tracee is known to be blocked for the duration of
sve_set_common(), it doesn't matter whether this is called at the start
or the end.
For consistency I've moved the setting of TIF_SVE earlier. This will be
cleared when loading FPSIMD-only state, and so moving this has no
resulting functional change.
Note that we only allocate the memory for SVE state when SVE register
contents are provided, avoiding unnecessary memory allocations for tasks
which only use FPSIMD.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Fixes: baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 5d0a8d2fba50 ("arm64/ptrace: Ensure that SME is set up for target when writing SSVE state")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-20-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:39 +01:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Allocate SME storage if necessary, preserving any existing ZA/ZT state */
|
|
|
|
if (type == ARM64_VEC_SME) {
|
|
|
|
sme_alloc(target, false);
|
|
|
|
if (!target->thread.sme_state)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate SVE storage if necessary, zeroing any existing SVE state */
|
|
|
|
if (!fpsimd) {
|
|
|
|
sve_alloc(target, true);
|
|
|
|
if (!target->thread.sve_state)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
2024-11-06 17:41:32 +00:00
|
|
|
/*
|
|
|
|
* Actual VL set may be different from what the user asked
|
|
|
|
* for, or we may have configured the _ONEXEC VL not the
|
|
|
|
* current VL:
|
|
|
|
*/
|
2022-04-19 12:22:28 +01:00
|
|
|
vq = sve_vq_from_vl(task_get_vl(target, type));
|
|
|
|
|
|
|
|
/* Enter/exit streaming mode */
|
|
|
|
if (system_supports_sme()) {
|
|
|
|
switch (type) {
|
|
|
|
case ARM64_VEC_SVE:
|
2022-05-10 17:12:01 +01:00
|
|
|
target->thread.svcr &= ~SVCR_SM_MASK;
|
arm64/fpsimd: ptrace: Gracefully handle errors
Within sve_set_common() we do not handle error conditions correctly:
* When writing to NT_ARM_SSVE, if sme_alloc() fails, the task will be
left with task->thread.sme_state==NULL, but TIF_SME will be set and
task->thread.fp_type==FP_STATE_SVE. This will result in a subsequent
null pointer dereference when the task's state is loaded or otherwise
manipulated.
* When writing to NT_ARM_SSVE, if sve_alloc() fails, the task will be
left with task->thread.sve_state==NULL, but TIF_SME will be set,
PSTATE.SM will be set, and task->thread.fp_type==FP_STATE_FPSIMD.
This is not a legitimate state, and can result in various problems,
including a subsequent null pointer dereference and/or the task
inheriting stale streaming mode register state the next time its state
is loaded into hardware.
* When writing to NT_ARM_SSVE, if the VL is changed but the resulting VL
differs from that in the header, the task will be left with TIF_SME
set, PSTATE.SM set, but task->thread.fp_type==FP_STATE_FPSIMD. This is
not a legitimate state, and can result in various problems as
described above.
Avoid these problems by allocating memory earlier, and by changing the
task's saved fp_type to FP_STATE_SVE before skipping register writes due
to a change of VL.
To make early returns simpler, I've moved the call to
fpsimd_flush_task_state() earlier. As the tracee's state has already
been saved, and the tracee is known to be blocked for the duration of
sve_set_common(), it doesn't matter whether this is called at the start
or the end.
For consistency I've moved the setting of TIF_SVE earlier. This will be
cleared when loading FPSIMD-only state, and so moving this has no
resulting functional change.
Note that we only allocate the memory for SVE state when SVE register
contents are provided, avoiding unnecessary memory allocations for tasks
which only use FPSIMD.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Fixes: baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 5d0a8d2fba50 ("arm64/ptrace: Ensure that SME is set up for target when writing SSVE state")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-20-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:39 +01:00
|
|
|
set_tsk_thread_flag(target, TIF_SVE);
|
2022-04-19 12:22:28 +01:00
|
|
|
break;
|
|
|
|
case ARM64_VEC_SME:
|
2022-05-10 17:12:01 +01:00
|
|
|
target->thread.svcr |= SVCR_SM_MASK;
|
2023-08-10 12:28:19 +01:00
|
|
|
set_tsk_thread_flag(target, TIF_SME);
|
2022-04-19 12:22:28 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN_ON_ONCE(1);
|
arm64/fpsimd: ptrace: Gracefully handle errors
Within sve_set_common() we do not handle error conditions correctly:
* When writing to NT_ARM_SSVE, if sme_alloc() fails, the task will be
left with task->thread.sme_state==NULL, but TIF_SME will be set and
task->thread.fp_type==FP_STATE_SVE. This will result in a subsequent
null pointer dereference when the task's state is loaded or otherwise
manipulated.
* When writing to NT_ARM_SSVE, if sve_alloc() fails, the task will be
left with task->thread.sve_state==NULL, but TIF_SME will be set,
PSTATE.SM will be set, and task->thread.fp_type==FP_STATE_FPSIMD.
This is not a legitimate state, and can result in various problems,
including a subsequent null pointer dereference and/or the task
inheriting stale streaming mode register state the next time its state
is loaded into hardware.
* When writing to NT_ARM_SSVE, if the VL is changed but the resulting VL
differs from that in the header, the task will be left with TIF_SME
set, PSTATE.SM set, but task->thread.fp_type==FP_STATE_FPSIMD. This is
not a legitimate state, and can result in various problems as
described above.
Avoid these problems by allocating memory earlier, and by changing the
task's saved fp_type to FP_STATE_SVE before skipping register writes due
to a change of VL.
To make early returns simpler, I've moved the call to
fpsimd_flush_task_state() earlier. As the tracee's state has already
been saved, and the tracee is known to be blocked for the duration of
sve_set_common(), it doesn't matter whether this is called at the start
or the end.
For consistency I've moved the setting of TIF_SVE earlier. This will be
cleared when loading FPSIMD-only state, and so moving this has no
resulting functional change.
Note that we only allocate the memory for SVE state when SVE register
contents are provided, avoiding unnecessary memory allocations for tasks
which only use FPSIMD.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Fixes: baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 5d0a8d2fba50 ("arm64/ptrace: Ensure that SME is set up for target when writing SSVE state")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-20-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:39 +01:00
|
|
|
return -EINVAL;
|
2022-04-19 12:22:28 +01:00
|
|
|
}
|
|
|
|
}
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
arm64/fpsimd: ptrace: Consistently handle partial writes to NT_ARM_(S)SVE
Partial writes to the NT_ARM_SVE and NT_ARM_SSVE regsets using an
payload are handled inconsistently and non-deterministically. A comment
within sve_set_common() indicates that we intended that a partial write
would preserve any effective FPSIMD/SVE state which was not overwritten,
but this has never worked consistently, and during syscalls the FPSIMD
vector state may be non-deterministically preserved and may be
erroneously migrated between streaming and non-streaming SVE modes.
The simplest fix is to handle a partial write by consistently zeroing
the remaining state. As detailed below I do not believe this will
adversely affect any real usage.
Neither GDB nor LLDB attempt partial writes to these regsets, and the
documentation (in Documentation/arch/arm64/sve.rst) has always indicated
that state preservation was not guaranteed, as is says:
| The effect of writing a partial, incomplete payload is unspecified.
When the logic was originally introduced in commit:
43d4da2c45b2 ("arm64/sve: ptrace and ELF coredump support")
... there were two potential behaviours, depending on TIF_SVE:
* When TIF_SVE was clear, all SVE state would be zeroed, excluding the
low 128 bits of vectors shared with FPSIMD, FPSR, and FPCR.
* When TIF_SVE was set, all SVE state would be zeroed, including the
low 128 bits of vectors shared with FPSIMD, but excluding FPSR and
FPCR.
Note that as writing to NT_ARM_SVE would set TIF_SVE, partial writes to
NT_ARM_SVE would not be idempotent, and if a first write preserved the
low 128 bits, a subsequent (potentially identical) partial write would
discard the low 128 bits.
When support for the NT_ARM_SSVE regset was added in commit:
e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
... the above behaviour was retained for writes to the NT_ARM_SVE
regset, though writes to the NT_ARM_SSVE would always zero the SVE
registers and would not inherit FPSIMD register state. This happened as
fpsimd_sync_to_sve() only copied the FPSIMD regs when TIF_SVE was clear
and PSTATE.SM==0.
Subsequently, when FPSIMD/SVE state tracking was changed across commits:
baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
a0136be443d5 (arm64/fpsimd: Load FP state based on recorded data type")
bbc6172eefdb ("arm64/fpsimd: SME no longer requires SVE register state")
8c845e273104 ("arm64/sve: Leave SVE enabled on syscall if we don't context switch")
... there was no corresponding update to the ptrace code, nor to
fpsimd_sync_to_sve(), which stil considers TIF_SVE and PSTATE.SM rather
than the saved fp_type. The saved state can be in the FPSIMD format
regardless of whether TIF_SVE is set or clear, and the saved type can
change non-deterministically during syscalls. Consequently a subsequent
partial write to the NT_ARM_SVE or NT_ARM_SSVE regsets may
non-deterministically preserve the FPSIMD state, and may migrate this
state between streaming and non-streaming modes.
Clean this up by never attempting to preserve ANY state when writing an
SVE payload to the NT_ARM_SVE/NT_ARM_SSVE regsets, zeroing all relevant
state including FPSR and FPCR. This simplifies the code, makes the
behaviour deterministic, and avoids migrating state between streaming
and non-streaming modes. As above, I do not believe this should
adversely affect existing userspace applications.
At the same time, remove fpsimd_sync_to_sve(). It is no longer used,
doesn't do what its documentation implies, and gets in the way of other
cleanups and fixes.
Fixes: 43d4da2c45b2 ("arm64/sve: ptrace and ELF coredump support")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-6-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:25 +01:00
|
|
|
/* Always zero V regs, FPSR, and FPCR */
|
|
|
|
memset(¤t->thread.uw.fpsimd_state, 0,
|
|
|
|
sizeof(current->thread.uw.fpsimd_state));
|
|
|
|
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
/* Registers: FPSIMD-only case */
|
|
|
|
|
|
|
|
BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header));
|
2025-05-08 14:26:38 +01:00
|
|
|
if (fpsimd) {
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
clear_tsk_thread_flag(target, TIF_SVE);
|
2022-11-15 09:46:34 +00:00
|
|
|
target->thread.fp_type = FP_STATE_FPSIMD;
|
arm64/fpsimd: ptrace: Consistently handle partial writes to NT_ARM_(S)SVE
Partial writes to the NT_ARM_SVE and NT_ARM_SSVE regsets using an
payload are handled inconsistently and non-deterministically. A comment
within sve_set_common() indicates that we intended that a partial write
would preserve any effective FPSIMD/SVE state which was not overwritten,
but this has never worked consistently, and during syscalls the FPSIMD
vector state may be non-deterministically preserved and may be
erroneously migrated between streaming and non-streaming SVE modes.
The simplest fix is to handle a partial write by consistently zeroing
the remaining state. As detailed below I do not believe this will
adversely affect any real usage.
Neither GDB nor LLDB attempt partial writes to these regsets, and the
documentation (in Documentation/arch/arm64/sve.rst) has always indicated
that state preservation was not guaranteed, as is says:
| The effect of writing a partial, incomplete payload is unspecified.
When the logic was originally introduced in commit:
43d4da2c45b2 ("arm64/sve: ptrace and ELF coredump support")
... there were two potential behaviours, depending on TIF_SVE:
* When TIF_SVE was clear, all SVE state would be zeroed, excluding the
low 128 bits of vectors shared with FPSIMD, FPSR, and FPCR.
* When TIF_SVE was set, all SVE state would be zeroed, including the
low 128 bits of vectors shared with FPSIMD, but excluding FPSR and
FPCR.
Note that as writing to NT_ARM_SVE would set TIF_SVE, partial writes to
NT_ARM_SVE would not be idempotent, and if a first write preserved the
low 128 bits, a subsequent (potentially identical) partial write would
discard the low 128 bits.
When support for the NT_ARM_SSVE regset was added in commit:
e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
... the above behaviour was retained for writes to the NT_ARM_SVE
regset, though writes to the NT_ARM_SSVE would always zero the SVE
registers and would not inherit FPSIMD register state. This happened as
fpsimd_sync_to_sve() only copied the FPSIMD regs when TIF_SVE was clear
and PSTATE.SM==0.
Subsequently, when FPSIMD/SVE state tracking was changed across commits:
baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
a0136be443d5 (arm64/fpsimd: Load FP state based on recorded data type")
bbc6172eefdb ("arm64/fpsimd: SME no longer requires SVE register state")
8c845e273104 ("arm64/sve: Leave SVE enabled on syscall if we don't context switch")
... there was no corresponding update to the ptrace code, nor to
fpsimd_sync_to_sve(), which stil considers TIF_SVE and PSTATE.SM rather
than the saved fp_type. The saved state can be in the FPSIMD format
regardless of whether TIF_SVE is set or clear, and the saved type can
change non-deterministically during syscalls. Consequently a subsequent
partial write to the NT_ARM_SVE or NT_ARM_SSVE regsets may
non-deterministically preserve the FPSIMD state, and may migrate this
state between streaming and non-streaming modes.
Clean this up by never attempting to preserve ANY state when writing an
SVE payload to the NT_ARM_SVE/NT_ARM_SSVE regsets, zeroing all relevant
state including FPSR and FPCR. This simplifies the code, makes the
behaviour deterministic, and avoids migrating state between streaming
and non-streaming modes. As above, I do not believe this should
adversely affect existing userspace applications.
At the same time, remove fpsimd_sync_to_sve(). It is no longer used,
doesn't do what its documentation implies, and gets in the way of other
cleanups and fixes.
Fixes: 43d4da2c45b2 ("arm64/sve: ptrace and ELF coredump support")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-6-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:25 +01:00
|
|
|
ret = __fpr_set(target, regset, pos, count, kbuf, ubuf,
|
|
|
|
SVE_PT_FPSIMD_OFFSET);
|
arm64/fpsimd: ptrace: Gracefully handle errors
Within sve_set_common() we do not handle error conditions correctly:
* When writing to NT_ARM_SSVE, if sme_alloc() fails, the task will be
left with task->thread.sme_state==NULL, but TIF_SME will be set and
task->thread.fp_type==FP_STATE_SVE. This will result in a subsequent
null pointer dereference when the task's state is loaded or otherwise
manipulated.
* When writing to NT_ARM_SSVE, if sve_alloc() fails, the task will be
left with task->thread.sve_state==NULL, but TIF_SME will be set,
PSTATE.SM will be set, and task->thread.fp_type==FP_STATE_FPSIMD.
This is not a legitimate state, and can result in various problems,
including a subsequent null pointer dereference and/or the task
inheriting stale streaming mode register state the next time its state
is loaded into hardware.
* When writing to NT_ARM_SSVE, if the VL is changed but the resulting VL
differs from that in the header, the task will be left with TIF_SME
set, PSTATE.SM set, but task->thread.fp_type==FP_STATE_FPSIMD. This is
not a legitimate state, and can result in various problems as
described above.
Avoid these problems by allocating memory earlier, and by changing the
task's saved fp_type to FP_STATE_SVE before skipping register writes due
to a change of VL.
To make early returns simpler, I've moved the call to
fpsimd_flush_task_state() earlier. As the tracee's state has already
been saved, and the tracee is known to be blocked for the duration of
sve_set_common(), it doesn't matter whether this is called at the start
or the end.
For consistency I've moved the setting of TIF_SVE earlier. This will be
cleared when loading FPSIMD-only state, and so moving this has no
resulting functional change.
Note that we only allocate the memory for SVE state when SVE register
contents are provided, avoiding unnecessary memory allocations for tasks
which only use FPSIMD.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Fixes: baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 5d0a8d2fba50 ("arm64/ptrace: Ensure that SME is set up for target when writing SSVE state")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-20-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:39 +01:00
|
|
|
return ret;
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
}
|
|
|
|
|
arm64/fpsimd: ptrace: Do not present register data for inactive mode
The SME ptrace ABI is written around the incorrect assumption that
SVE_PT_REGS_FPSIMD and SVE_PT_REGS_SVE are independent bit flags, where
it is possible for both to be clear. In reality they are different
values for bit 0 of the header flags, where SVE_PT_REGS_FPSIMD is 0 and
SVE_PT_REGS_SVE is 1. In cases where code was written expecting that
neither bit flag would be set, the value is equivalent to
SVE_PT_REGS_FPSIMD.
One consequence of this is that reads of the NT_ARM_SVE or NT_ARM_SSVE
will erroneously present data from the other mode:
* When PSTATE.SM==1, reads of NT_ARM_SVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from streaming mode.
* When PSTATE.SM==0, reads of NT_ARM_SSVE will present a header with
SVE_PT_REGS_FPSIMD, and FPSIMD-formatted data from non-streaming mode.
The original intent was that no register data would be provided in these
cases, as described in commit:
e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Luckily, debuggers do not consume the bogus register data. Both GDB and
LLDB read the NT_ARM_SSVE regset before the NT_ARM_SVE regset, and
assume that when the NT_ARM_SSVE header presents SVE_PT_REGS_FPSIMD, it
is necessary to read register contents from the NT_ARM_SVE regset,
regardless of whether the NT_ARM_SSVE regset provided bogus register
data.
Fix the code to stop presenting register data from the inactive mode.
At the same time, make the manipulation of the flag clearer, and remove
the bogus comment from sve_set_common(). I've given this a quick spin
with GDB and LLDB, and both seem happy.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-18-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:37 +01:00
|
|
|
/* Otherwise: no registers or full SVE case. */
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
arm64/fpsimd: ptrace: Gracefully handle errors
Within sve_set_common() we do not handle error conditions correctly:
* When writing to NT_ARM_SSVE, if sme_alloc() fails, the task will be
left with task->thread.sme_state==NULL, but TIF_SME will be set and
task->thread.fp_type==FP_STATE_SVE. This will result in a subsequent
null pointer dereference when the task's state is loaded or otherwise
manipulated.
* When writing to NT_ARM_SSVE, if sve_alloc() fails, the task will be
left with task->thread.sve_state==NULL, but TIF_SME will be set,
PSTATE.SM will be set, and task->thread.fp_type==FP_STATE_FPSIMD.
This is not a legitimate state, and can result in various problems,
including a subsequent null pointer dereference and/or the task
inheriting stale streaming mode register state the next time its state
is loaded into hardware.
* When writing to NT_ARM_SSVE, if the VL is changed but the resulting VL
differs from that in the header, the task will be left with TIF_SME
set, PSTATE.SM set, but task->thread.fp_type==FP_STATE_FPSIMD. This is
not a legitimate state, and can result in various problems as
described above.
Avoid these problems by allocating memory earlier, and by changing the
task's saved fp_type to FP_STATE_SVE before skipping register writes due
to a change of VL.
To make early returns simpler, I've moved the call to
fpsimd_flush_task_state() earlier. As the tracee's state has already
been saved, and the tracee is known to be blocked for the duration of
sve_set_common(), it doesn't matter whether this is called at the start
or the end.
For consistency I've moved the setting of TIF_SVE earlier. This will be
cleared when loading FPSIMD-only state, and so moving this has no
resulting functional change.
Note that we only allocate the memory for SVE state when SVE register
contents are provided, avoiding unnecessary memory allocations for tasks
which only use FPSIMD.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Fixes: baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 5d0a8d2fba50 ("arm64/ptrace: Ensure that SME is set up for target when writing SSVE state")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-20-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:39 +01:00
|
|
|
target->thread.fp_type = FP_STATE_SVE;
|
|
|
|
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
/*
|
|
|
|
* If setting a different VL from the requested VL and there is
|
|
|
|
* register data, the data layout will be wrong: don't even
|
|
|
|
* try to set the registers in this case.
|
|
|
|
*/
|
arm64/fpsimd: ptrace: Gracefully handle errors
Within sve_set_common() we do not handle error conditions correctly:
* When writing to NT_ARM_SSVE, if sme_alloc() fails, the task will be
left with task->thread.sme_state==NULL, but TIF_SME will be set and
task->thread.fp_type==FP_STATE_SVE. This will result in a subsequent
null pointer dereference when the task's state is loaded or otherwise
manipulated.
* When writing to NT_ARM_SSVE, if sve_alloc() fails, the task will be
left with task->thread.sve_state==NULL, but TIF_SME will be set,
PSTATE.SM will be set, and task->thread.fp_type==FP_STATE_FPSIMD.
This is not a legitimate state, and can result in various problems,
including a subsequent null pointer dereference and/or the task
inheriting stale streaming mode register state the next time its state
is loaded into hardware.
* When writing to NT_ARM_SSVE, if the VL is changed but the resulting VL
differs from that in the header, the task will be left with TIF_SME
set, PSTATE.SM set, but task->thread.fp_type==FP_STATE_FPSIMD. This is
not a legitimate state, and can result in various problems as
described above.
Avoid these problems by allocating memory earlier, and by changing the
task's saved fp_type to FP_STATE_SVE before skipping register writes due
to a change of VL.
To make early returns simpler, I've moved the call to
fpsimd_flush_task_state() earlier. As the tracee's state has already
been saved, and the tracee is known to be blocked for the duration of
sve_set_common(), it doesn't matter whether this is called at the start
or the end.
For consistency I've moved the setting of TIF_SVE earlier. This will be
cleared when loading FPSIMD-only state, and so moving this has no
resulting functional change.
Note that we only allocate the memory for SVE state when SVE register
contents are provided, avoiding unnecessary memory allocations for tasks
which only use FPSIMD.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Fixes: baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 5d0a8d2fba50 ("arm64/ptrace: Ensure that SME is set up for target when writing SSVE state")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-20-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:39 +01:00
|
|
|
if (count && vq != sve_vq_from_vl(header.vl))
|
|
|
|
return -EIO;
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
|
|
|
BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header));
|
|
|
|
start = SVE_PT_SVE_OFFSET;
|
|
|
|
end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq);
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
target->thread.sve_state,
|
|
|
|
start, end);
|
|
|
|
if (ret)
|
arm64/fpsimd: ptrace: Gracefully handle errors
Within sve_set_common() we do not handle error conditions correctly:
* When writing to NT_ARM_SSVE, if sme_alloc() fails, the task will be
left with task->thread.sme_state==NULL, but TIF_SME will be set and
task->thread.fp_type==FP_STATE_SVE. This will result in a subsequent
null pointer dereference when the task's state is loaded or otherwise
manipulated.
* When writing to NT_ARM_SSVE, if sve_alloc() fails, the task will be
left with task->thread.sve_state==NULL, but TIF_SME will be set,
PSTATE.SM will be set, and task->thread.fp_type==FP_STATE_FPSIMD.
This is not a legitimate state, and can result in various problems,
including a subsequent null pointer dereference and/or the task
inheriting stale streaming mode register state the next time its state
is loaded into hardware.
* When writing to NT_ARM_SSVE, if the VL is changed but the resulting VL
differs from that in the header, the task will be left with TIF_SME
set, PSTATE.SM set, but task->thread.fp_type==FP_STATE_FPSIMD. This is
not a legitimate state, and can result in various problems as
described above.
Avoid these problems by allocating memory earlier, and by changing the
task's saved fp_type to FP_STATE_SVE before skipping register writes due
to a change of VL.
To make early returns simpler, I've moved the call to
fpsimd_flush_task_state() earlier. As the tracee's state has already
been saved, and the tracee is known to be blocked for the duration of
sve_set_common(), it doesn't matter whether this is called at the start
or the end.
For consistency I've moved the setting of TIF_SVE earlier. This will be
cleared when loading FPSIMD-only state, and so moving this has no
resulting functional change.
Note that we only allocate the memory for SVE state when SVE register
contents are provided, avoiding unnecessary memory allocations for tasks
which only use FPSIMD.
Fixes: e12310a0d30f ("arm64/sme: Implement ptrace support for streaming mode SVE registers")
Fixes: baa8515281b3 ("arm64/fpsimd: Track the saved FPSIMD state type separately to TIF_SVE")
Fixes: 5d0a8d2fba50 ("arm64/ptrace: Ensure that SME is set up for target when writing SSVE state")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: David Spickett <david.spickett@arm.com>
Cc: Luis Machado <luis.machado@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20250508132644.1395904-20-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2025-05-08 14:26:39 +01:00
|
|
|
return ret;
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
|
|
|
start = end;
|
|
|
|
end = SVE_PT_SVE_FPSR_OFFSET(vq);
|
2022-10-15 00:22:25 +03:00
|
|
|
user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, start, end);
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy fpsr, and fpcr which must follow contiguously in
|
|
|
|
* struct fpsimd_state:
|
|
|
|
*/
|
|
|
|
start = end;
|
|
|
|
end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE;
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
2018-03-28 10:50:49 +01:00
|
|
|
&target->thread.uw.fpsimd_state.fpsr,
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
start, end);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
static int sve_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
if (!system_supports_sve())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return sve_set_common(target, regset, pos, count, kbuf, ubuf,
|
|
|
|
ARM64_VEC_SVE);
|
|
|
|
}
|
|
|
|
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
#endif /* CONFIG_ARM64_SVE */
|
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
#ifdef CONFIG_ARM64_SME
|
|
|
|
|
|
|
|
static int ssve_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
struct membuf to)
|
|
|
|
{
|
|
|
|
if (!system_supports_sme())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return sve_get_common(target, regset, to, ARM64_VEC_SME);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ssve_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
if (!system_supports_sme())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return sve_set_common(target, regset, pos, count, kbuf, ubuf,
|
|
|
|
ARM64_VEC_SME);
|
|
|
|
}
|
|
|
|
|
2022-04-19 12:22:29 +01:00
|
|
|
static int za_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
struct membuf to)
|
|
|
|
{
|
|
|
|
struct user_za_header header;
|
|
|
|
unsigned int vq;
|
|
|
|
unsigned long start, end;
|
|
|
|
|
|
|
|
if (!system_supports_sme())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Header */
|
|
|
|
memset(&header, 0, sizeof(header));
|
|
|
|
|
|
|
|
if (test_tsk_thread_flag(target, TIF_SME_VL_INHERIT))
|
|
|
|
header.flags |= ZA_PT_VL_INHERIT;
|
|
|
|
|
|
|
|
header.vl = task_get_sme_vl(target);
|
|
|
|
vq = sve_vq_from_vl(header.vl);
|
|
|
|
header.max_vl = sme_max_vl();
|
|
|
|
header.max_size = ZA_PT_SIZE(vq);
|
|
|
|
|
|
|
|
/* If ZA is not active there is only the header */
|
|
|
|
if (thread_za_enabled(&target->thread))
|
|
|
|
header.size = ZA_PT_SIZE(vq);
|
|
|
|
else
|
|
|
|
header.size = ZA_PT_ZA_OFFSET;
|
|
|
|
|
|
|
|
membuf_write(&to, &header, sizeof(header));
|
|
|
|
|
|
|
|
BUILD_BUG_ON(ZA_PT_ZA_OFFSET != sizeof(header));
|
|
|
|
end = ZA_PT_ZA_OFFSET;
|
|
|
|
|
|
|
|
if (target == current)
|
|
|
|
fpsimd_preserve_current_state();
|
|
|
|
|
|
|
|
/* Any register data to include? */
|
|
|
|
if (thread_za_enabled(&target->thread)) {
|
|
|
|
start = end;
|
|
|
|
end = ZA_PT_SIZE(vq);
|
2023-01-16 16:04:36 +00:00
|
|
|
membuf_write(&to, target->thread.sme_state, end - start);
|
2022-04-19 12:22:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Zero any trailing padding */
|
|
|
|
start = end;
|
|
|
|
end = ALIGN(header.size, SVE_VQ_BYTES);
|
|
|
|
return membuf_zero(&to, end - start);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int za_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct user_za_header header;
|
|
|
|
unsigned int vq;
|
|
|
|
unsigned long start, end;
|
|
|
|
|
|
|
|
if (!system_supports_sme())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Header */
|
|
|
|
if (count < sizeof(header))
|
|
|
|
return -EINVAL;
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &header,
|
|
|
|
0, sizeof(header));
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All current ZA_PT_* flags are consumed by
|
|
|
|
* vec_set_vector_length(), which will also validate them for
|
|
|
|
* us:
|
|
|
|
*/
|
|
|
|
ret = vec_set_vector_length(target, ARM64_VEC_SME, header.vl,
|
|
|
|
((unsigned long)header.flags) << 16);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2024-11-06 17:41:32 +00:00
|
|
|
/*
|
|
|
|
* Actual VL set may be different from what the user asked
|
|
|
|
* for, or we may have configured the _ONEXEC rather than
|
|
|
|
* current VL:
|
|
|
|
*/
|
2022-04-19 12:22:29 +01:00
|
|
|
vq = sve_vq_from_vl(task_get_sme_vl(target));
|
|
|
|
|
|
|
|
/* Ensure there is some SVE storage for streaming mode */
|
|
|
|
if (!target->thread.sve_state) {
|
2022-08-17 19:23:23 +01:00
|
|
|
sve_alloc(target, false);
|
2022-04-19 12:22:29 +01:00
|
|
|
if (!target->thread.sve_state) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-15 18:42:38 +00:00
|
|
|
/*
|
|
|
|
* Only flush the storage if PSTATE.ZA was not already set,
|
|
|
|
* otherwise preserve any existing data.
|
|
|
|
*/
|
|
|
|
sme_alloc(target, !thread_za_enabled(&target->thread));
|
|
|
|
if (!target->thread.sme_state)
|
|
|
|
return -ENOMEM;
|
2022-04-19 12:22:29 +01:00
|
|
|
|
|
|
|
/* If there is no data then disable ZA */
|
|
|
|
if (!count) {
|
2022-05-10 17:12:01 +01:00
|
|
|
target->thread.svcr &= ~SVCR_ZA_MASK;
|
2022-04-19 12:22:29 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If setting a different VL from the requested VL and there is
|
|
|
|
* register data, the data layout will be wrong: don't even
|
|
|
|
* try to set the registers in this case.
|
|
|
|
*/
|
|
|
|
if (vq != sve_vq_from_vl(header.vl)) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
BUILD_BUG_ON(ZA_PT_ZA_OFFSET != sizeof(header));
|
|
|
|
start = ZA_PT_ZA_OFFSET;
|
|
|
|
end = ZA_PT_SIZE(vq);
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
2023-01-16 16:04:36 +00:00
|
|
|
target->thread.sme_state,
|
2022-04-19 12:22:29 +01:00
|
|
|
start, end);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Mark ZA as active and let userspace use it */
|
|
|
|
set_tsk_thread_flag(target, TIF_SME);
|
2022-05-10 17:12:01 +01:00
|
|
|
target->thread.svcr |= SVCR_ZA_MASK;
|
2022-04-19 12:22:29 +01:00
|
|
|
|
|
|
|
out:
|
|
|
|
fpsimd_flush_task_state(target);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-01-16 16:04:47 +00:00
|
|
|
static int zt_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
struct membuf to)
|
|
|
|
{
|
|
|
|
if (!system_supports_sme2())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If PSTATE.ZA is not set then ZT will be zeroed when it is
|
|
|
|
* enabled so report the current register value as zero.
|
|
|
|
*/
|
|
|
|
if (thread_za_enabled(&target->thread))
|
|
|
|
membuf_write(&to, thread_zt_state(&target->thread),
|
|
|
|
ZT_SIG_REG_BYTES);
|
|
|
|
else
|
|
|
|
membuf_zero(&to, ZT_SIG_REG_BYTES);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int zt_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!system_supports_sme2())
|
|
|
|
return -EINVAL;
|
|
|
|
|
2023-08-16 19:40:07 +01:00
|
|
|
/* Ensure SVE storage in case this is first use of SME */
|
|
|
|
sve_alloc(target, false);
|
|
|
|
if (!target->thread.sve_state)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2023-01-16 16:04:47 +00:00
|
|
|
if (!thread_za_enabled(&target->thread)) {
|
2023-08-10 12:28:19 +01:00
|
|
|
sme_alloc(target, true);
|
2023-01-16 16:04:47 +00:00
|
|
|
if (!target->thread.sme_state)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
thread_zt_state(&target->thread),
|
|
|
|
0, ZT_SIG_REG_BYTES);
|
2023-08-16 19:40:07 +01:00
|
|
|
if (ret == 0) {
|
2023-01-16 16:04:47 +00:00
|
|
|
target->thread.svcr |= SVCR_ZA_MASK;
|
2023-08-16 19:40:07 +01:00
|
|
|
set_tsk_thread_flag(target, TIF_SME);
|
|
|
|
}
|
2023-01-16 16:04:47 +00:00
|
|
|
|
2023-08-03 01:19:06 +01:00
|
|
|
fpsimd_flush_task_state(target);
|
|
|
|
|
2023-01-16 16:04:47 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-04-19 12:22:28 +01:00
|
|
|
#endif /* CONFIG_ARM64_SME */
|
|
|
|
|
2018-12-07 18:39:26 +00:00
|
|
|
#ifdef CONFIG_ARM64_PTR_AUTH
|
|
|
|
static int pac_mask_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2018-12-07 18:39:26 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The PAC bits can differ across data and instruction pointers
|
|
|
|
* depending on TCR_EL1.TBID*, which we may make use of in future, so
|
|
|
|
* we expose separate masks.
|
|
|
|
*/
|
|
|
|
unsigned long mask = ptrauth_user_pac_mask();
|
|
|
|
struct user_pac_mask uregs = {
|
|
|
|
.data_mask = mask,
|
|
|
|
.insn_mask = mask,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!system_supports_address_auth())
|
|
|
|
return -EINVAL;
|
|
|
|
|
2020-05-27 19:09:44 -04:00
|
|
|
return membuf_write(&to, &uregs, sizeof(uregs));
|
2018-12-07 18:39:26 +00:00
|
|
|
}
|
2019-01-30 12:02:44 +00:00
|
|
|
|
2021-03-18 20:10:53 -07:00
|
|
|
static int pac_enabled_keys_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
struct membuf to)
|
|
|
|
{
|
|
|
|
long enabled_keys = ptrauth_get_enabled_keys(target);
|
|
|
|
|
|
|
|
if (IS_ERR_VALUE(enabled_keys))
|
|
|
|
return enabled_keys;
|
|
|
|
|
|
|
|
return membuf_write(&to, &enabled_keys, sizeof(enabled_keys));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pac_enabled_keys_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
long enabled_keys = ptrauth_get_enabled_keys(target);
|
|
|
|
|
|
|
|
if (IS_ERR_VALUE(enabled_keys))
|
|
|
|
return enabled_keys;
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &enabled_keys, 0,
|
|
|
|
sizeof(long));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return ptrauth_set_enabled_keys(target, PR_PAC_ENABLED_KEYS_MASK,
|
|
|
|
enabled_keys);
|
|
|
|
}
|
|
|
|
|
2019-01-30 12:02:44 +00:00
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
|
|
|
static __uint128_t pac_key_to_user(const struct ptrauth_key *key)
|
|
|
|
{
|
|
|
|
return (__uint128_t)key->hi << 64 | key->lo;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ptrauth_key pac_key_from_user(__uint128_t ukey)
|
|
|
|
{
|
|
|
|
struct ptrauth_key key = {
|
|
|
|
.lo = (unsigned long)ukey,
|
|
|
|
.hi = (unsigned long)(ukey >> 64),
|
|
|
|
};
|
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pac_address_keys_to_user(struct user_pac_address_keys *ukeys,
|
2020-03-13 14:34:50 +05:30
|
|
|
const struct ptrauth_keys_user *keys)
|
2019-01-30 12:02:44 +00:00
|
|
|
{
|
|
|
|
ukeys->apiakey = pac_key_to_user(&keys->apia);
|
|
|
|
ukeys->apibkey = pac_key_to_user(&keys->apib);
|
|
|
|
ukeys->apdakey = pac_key_to_user(&keys->apda);
|
|
|
|
ukeys->apdbkey = pac_key_to_user(&keys->apdb);
|
|
|
|
}
|
|
|
|
|
2020-03-13 14:34:50 +05:30
|
|
|
static void pac_address_keys_from_user(struct ptrauth_keys_user *keys,
|
2019-01-30 12:02:44 +00:00
|
|
|
const struct user_pac_address_keys *ukeys)
|
|
|
|
{
|
|
|
|
keys->apia = pac_key_from_user(ukeys->apiakey);
|
|
|
|
keys->apib = pac_key_from_user(ukeys->apibkey);
|
|
|
|
keys->apda = pac_key_from_user(ukeys->apdakey);
|
|
|
|
keys->apdb = pac_key_from_user(ukeys->apdbkey);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pac_address_keys_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2019-01-30 12:02:44 +00:00
|
|
|
{
|
2020-03-13 14:34:50 +05:30
|
|
|
struct ptrauth_keys_user *keys = &target->thread.keys_user;
|
2019-01-30 12:02:44 +00:00
|
|
|
struct user_pac_address_keys user_keys;
|
|
|
|
|
|
|
|
if (!system_supports_address_auth())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
pac_address_keys_to_user(&user_keys, keys);
|
|
|
|
|
2020-05-27 19:09:44 -04:00
|
|
|
return membuf_write(&to, &user_keys, sizeof(user_keys));
|
2019-01-30 12:02:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int pac_address_keys_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
2020-03-13 14:34:50 +05:30
|
|
|
struct ptrauth_keys_user *keys = &target->thread.keys_user;
|
2019-01-30 12:02:44 +00:00
|
|
|
struct user_pac_address_keys user_keys;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!system_supports_address_auth())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
pac_address_keys_to_user(&user_keys, keys);
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&user_keys, 0, -1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
pac_address_keys_from_user(keys, &user_keys);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pac_generic_keys_to_user(struct user_pac_generic_keys *ukeys,
|
2020-03-13 14:34:50 +05:30
|
|
|
const struct ptrauth_keys_user *keys)
|
2019-01-30 12:02:44 +00:00
|
|
|
{
|
|
|
|
ukeys->apgakey = pac_key_to_user(&keys->apga);
|
|
|
|
}
|
|
|
|
|
2020-03-13 14:34:50 +05:30
|
|
|
static void pac_generic_keys_from_user(struct ptrauth_keys_user *keys,
|
2019-01-30 12:02:44 +00:00
|
|
|
const struct user_pac_generic_keys *ukeys)
|
|
|
|
{
|
|
|
|
keys->apga = pac_key_from_user(ukeys->apgakey);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pac_generic_keys_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2019-01-30 12:02:44 +00:00
|
|
|
{
|
2020-03-13 14:34:50 +05:30
|
|
|
struct ptrauth_keys_user *keys = &target->thread.keys_user;
|
2019-01-30 12:02:44 +00:00
|
|
|
struct user_pac_generic_keys user_keys;
|
|
|
|
|
|
|
|
if (!system_supports_generic_auth())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
pac_generic_keys_to_user(&user_keys, keys);
|
|
|
|
|
2020-05-27 19:09:44 -04:00
|
|
|
return membuf_write(&to, &user_keys, sizeof(user_keys));
|
2019-01-30 12:02:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int pac_generic_keys_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
2020-03-13 14:34:50 +05:30
|
|
|
struct ptrauth_keys_user *keys = &target->thread.keys_user;
|
2019-01-30 12:02:44 +00:00
|
|
|
struct user_pac_generic_keys user_keys;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!system_supports_generic_auth())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
pac_generic_keys_to_user(&user_keys, keys);
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
|
&user_keys, 0, -1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
pac_generic_keys_from_user(keys, &user_keys);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_CHECKPOINT_RESTORE */
|
2018-12-07 18:39:26 +00:00
|
|
|
#endif /* CONFIG_ARM64_PTR_AUTH */
|
|
|
|
|
2020-07-03 15:12:57 +01:00
|
|
|
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
|
|
|
|
static int tagged_addr_ctrl_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
struct membuf to)
|
|
|
|
{
|
|
|
|
long ctrl = get_tagged_addr_ctrl(target);
|
|
|
|
|
arm64: ptrace: fix partial SETREGSET for NT_ARM_TAGGED_ADDR_CTRL
Currently tagged_addr_ctrl_set() doesn't initialize the temporary 'ctrl'
variable, and a SETREGSET call with a length of zero will leave this
uninitialized. Consequently tagged_addr_ctrl_set() will consume an
arbitrary value, potentially leaking up to 64 bits of memory from the
kernel stack. The read is limited to a specific slot on the stack, and
the issue does not provide a write mechanism.
As set_tagged_addr_ctrl() only accepts values where bits [63:4] zero and
rejects other values, a partial SETREGSET attempt will randomly succeed
or fail depending on the value of the uninitialized value, and the
exposure is significantly limited.
Fix this by initializing the temporary value before copying the regset
from userspace, as for other regsets (e.g. NT_PRSTATUS, NT_PRFPREG,
NT_ARM_SYSTEM_CALL). In the case of a zero-length write, the existing
value of the tagged address ctrl will be retained.
The NT_ARM_TAGGED_ADDR_CTRL regset is only visible in the
user_aarch64_view used by a native AArch64 task to manipulate another
native AArch64 task. As get_tagged_addr_ctrl() only returns an error
value when called for a compat task, tagged_addr_ctrl_get() and
tagged_addr_ctrl_set() should never observe an error value from
get_tagged_addr_ctrl(). Add a WARN_ON_ONCE() to both to indicate that
such an error would be unexpected, and error handlnig is not missing in
either case.
Fixes: 2200aa7154cb ("arm64: mte: ptrace: Add NT_ARM_TAGGED_ADDR_CTRL regset")
Cc: <stable@vger.kernel.org> # 5.10.x
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241205121655.1824269-2-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2024-12-05 12:16:52 +00:00
|
|
|
if (WARN_ON_ONCE(IS_ERR_VALUE(ctrl)))
|
2020-07-03 15:12:57 +01:00
|
|
|
return ctrl;
|
|
|
|
|
|
|
|
return membuf_write(&to, &ctrl, sizeof(ctrl));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tagged_addr_ctrl_set(struct task_struct *target, const struct
|
|
|
|
user_regset *regset, unsigned int pos,
|
|
|
|
unsigned int count, const void *kbuf, const
|
|
|
|
void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
long ctrl;
|
|
|
|
|
arm64: ptrace: fix partial SETREGSET for NT_ARM_TAGGED_ADDR_CTRL
Currently tagged_addr_ctrl_set() doesn't initialize the temporary 'ctrl'
variable, and a SETREGSET call with a length of zero will leave this
uninitialized. Consequently tagged_addr_ctrl_set() will consume an
arbitrary value, potentially leaking up to 64 bits of memory from the
kernel stack. The read is limited to a specific slot on the stack, and
the issue does not provide a write mechanism.
As set_tagged_addr_ctrl() only accepts values where bits [63:4] zero and
rejects other values, a partial SETREGSET attempt will randomly succeed
or fail depending on the value of the uninitialized value, and the
exposure is significantly limited.
Fix this by initializing the temporary value before copying the regset
from userspace, as for other regsets (e.g. NT_PRSTATUS, NT_PRFPREG,
NT_ARM_SYSTEM_CALL). In the case of a zero-length write, the existing
value of the tagged address ctrl will be retained.
The NT_ARM_TAGGED_ADDR_CTRL regset is only visible in the
user_aarch64_view used by a native AArch64 task to manipulate another
native AArch64 task. As get_tagged_addr_ctrl() only returns an error
value when called for a compat task, tagged_addr_ctrl_get() and
tagged_addr_ctrl_set() should never observe an error value from
get_tagged_addr_ctrl(). Add a WARN_ON_ONCE() to both to indicate that
such an error would be unexpected, and error handlnig is not missing in
either case.
Fixes: 2200aa7154cb ("arm64: mte: ptrace: Add NT_ARM_TAGGED_ADDR_CTRL regset")
Cc: <stable@vger.kernel.org> # 5.10.x
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241205121655.1824269-2-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2024-12-05 12:16:52 +00:00
|
|
|
ctrl = get_tagged_addr_ctrl(target);
|
|
|
|
if (WARN_ON_ONCE(IS_ERR_VALUE(ctrl)))
|
|
|
|
return ctrl;
|
|
|
|
|
2020-07-03 15:12:57 +01:00
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl, 0, -1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return set_tagged_addr_ctrl(target, ctrl);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2024-08-22 16:11:03 +01:00
|
|
|
#ifdef CONFIG_ARM64_POE
|
|
|
|
static int poe_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
struct membuf to)
|
|
|
|
{
|
|
|
|
if (!system_supports_poe())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return membuf_write(&to, &target->thread.por_el0,
|
|
|
|
sizeof(target->thread.por_el0));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int poe_set(struct task_struct *target, const struct
|
|
|
|
user_regset *regset, unsigned int pos,
|
|
|
|
unsigned int count, const void *kbuf, const
|
|
|
|
void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
long ctrl;
|
|
|
|
|
|
|
|
if (!system_supports_poe())
|
|
|
|
return -EINVAL;
|
|
|
|
|
arm64: ptrace: fix partial SETREGSET for NT_ARM_POE
Currently poe_set() doesn't initialize the temporary 'ctrl' variable,
and a SETREGSET call with a length of zero will leave this
uninitialized. Consequently an arbitrary value will be written back to
target->thread.por_el0, potentially leaking up to 64 bits of memory from
the kernel stack. The read is limited to a specific slot on the stack,
and the issue does not provide a write mechanism.
Fix this by initializing the temporary value before copying the regset
from userspace, as for other regsets (e.g. NT_PRSTATUS, NT_PRFPREG,
NT_ARM_SYSTEM_CALL). In the case of a zero-length write, the existing
contents of POR_EL1 will be retained.
Before this patch:
| # ./poe-test
| Attempting to write NT_ARM_POE::por_el0 = 0x900d900d900d900d
| SETREGSET(nt=0x40f, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_POE::por_el0
| GETREGSET(nt=0x40f, len=8) read 8 bytes
| Read NT_ARM_POE::por_el0 = 0x900d900d900d900d
|
| Attempting to write NT_ARM_POE (zero length)
| SETREGSET(nt=0x40f, len=0) wrote 0 bytes
|
| Attempting to read NT_ARM_POE::por_el0
| GETREGSET(nt=0x40f, len=8) read 8 bytes
| Read NT_ARM_POE::por_el0 = 0xffff8000839c3d50
After this patch:
| # ./poe-test
| Attempting to write NT_ARM_POE::por_el0 = 0x900d900d900d900d
| SETREGSET(nt=0x40f, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_POE::por_el0
| GETREGSET(nt=0x40f, len=8) read 8 bytes
| Read NT_ARM_POE::por_el0 = 0x900d900d900d900d
|
| Attempting to write NT_ARM_POE (zero length)
| SETREGSET(nt=0x40f, len=0) wrote 0 bytes
|
| Attempting to read NT_ARM_POE::por_el0
| GETREGSET(nt=0x40f, len=8) read 8 bytes
| Read NT_ARM_POE::por_el0 = 0x900d900d900d900d
Fixes: 175198199262 ("arm64/ptrace: add support for FEAT_POE")
Cc: <stable@vger.kernel.org> # 6.12.x
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Joey Gouly <joey.gouly@arm.com>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241205121655.1824269-4-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2024-12-05 12:16:54 +00:00
|
|
|
ctrl = target->thread.por_el0;
|
|
|
|
|
2024-08-22 16:11:03 +01:00
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl, 0, -1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
target->thread.por_el0 = ctrl;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2024-10-01 23:59:06 +01:00
|
|
|
#ifdef CONFIG_ARM64_GCS
|
arm64: ptrace: fix partial SETREGSET for NT_ARM_GCS
Currently gcs_set() doesn't initialize the temporary 'user_gcs'
variable, and a SETREGSET call with a length of 0, 8, or 16 will leave
some portion of this uninitialized. Consequently some arbitrary
uninitialized values may be written back to the relevant fields in task
struct, potentially leaking up to 192 bits of memory from the kernel
stack. The read is limited to a specific slot on the stack, and the
issue does not provide a write mechanism.
As gcs_set() rejects cases where user_gcs::features_enabled has bits set
other than PR_SHADOW_STACK_SUPPORTED_STATUS_MASK, a SETREGSET call with
a length of zero will randomly succeed or fail depending on the value of
the uninitialized value, it isn't possible to leak the full 192 bits.
With a length of 8 or 16, user_gcs::features_enabled can be initialized
to an accepted value, making it practical to leak 128 or 64 bits.
Fix this by initializing the temporary value before copying the regset
from userspace, as for other regsets (e.g. NT_PRSTATUS, NT_PRFPREG,
NT_ARM_SYSTEM_CALL). In the case of a zero-length or partial write, the
existing contents of the fields which are not written to will be
retained.
To ensure that the extraction and insertion of fields is consistent
across the GETREGSET and SETREGSET calls, new task_gcs_to_user() and
task_gcs_from_user() helpers are added, matching the style of
pac_address_keys_to_user() and pac_address_keys_from_user().
Before this patch:
| # ./gcs-test
| Attempting to write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
| SETREGSET(nt=0x410, len=24) wrote 24 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
|
| Attempting partial write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x1de7ec7edbadc0de,
| .gcspr_el0 = 0x1de7ec7edbadc0de,
| }
| SETREGSET(nt=0x410, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x000000000093e780,
| .gcspr_el0 = 0xffff800083a63d50,
| }
After this patch:
| # ./gcs-test
| Attempting to write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
| SETREGSET(nt=0x410, len=24) wrote 24 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
|
| Attempting partial write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x1de7ec7edbadc0de,
| .gcspr_el0 = 0x1de7ec7edbadc0de,
| }
| SETREGSET(nt=0x410, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
Fixes: 7ec3b57cb29f ("arm64/ptrace: Expose GCS via ptrace and core files")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241205121655.1824269-5-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2024-12-05 12:16:55 +00:00
|
|
|
static void task_gcs_to_user(struct user_gcs *user_gcs,
|
|
|
|
const struct task_struct *target)
|
|
|
|
{
|
|
|
|
user_gcs->features_enabled = target->thread.gcs_el0_mode;
|
|
|
|
user_gcs->features_locked = target->thread.gcs_el0_locked;
|
|
|
|
user_gcs->gcspr_el0 = target->thread.gcspr_el0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void task_gcs_from_user(struct task_struct *target,
|
|
|
|
const struct user_gcs *user_gcs)
|
|
|
|
{
|
|
|
|
target->thread.gcs_el0_mode = user_gcs->features_enabled;
|
|
|
|
target->thread.gcs_el0_locked = user_gcs->features_locked;
|
|
|
|
target->thread.gcspr_el0 = user_gcs->gcspr_el0;
|
|
|
|
}
|
|
|
|
|
2024-10-01 23:59:06 +01:00
|
|
|
static int gcs_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
struct membuf to)
|
|
|
|
{
|
|
|
|
struct user_gcs user_gcs;
|
|
|
|
|
|
|
|
if (!system_supports_gcs())
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (target == current)
|
|
|
|
gcs_preserve_current_state();
|
|
|
|
|
arm64: ptrace: fix partial SETREGSET for NT_ARM_GCS
Currently gcs_set() doesn't initialize the temporary 'user_gcs'
variable, and a SETREGSET call with a length of 0, 8, or 16 will leave
some portion of this uninitialized. Consequently some arbitrary
uninitialized values may be written back to the relevant fields in task
struct, potentially leaking up to 192 bits of memory from the kernel
stack. The read is limited to a specific slot on the stack, and the
issue does not provide a write mechanism.
As gcs_set() rejects cases where user_gcs::features_enabled has bits set
other than PR_SHADOW_STACK_SUPPORTED_STATUS_MASK, a SETREGSET call with
a length of zero will randomly succeed or fail depending on the value of
the uninitialized value, it isn't possible to leak the full 192 bits.
With a length of 8 or 16, user_gcs::features_enabled can be initialized
to an accepted value, making it practical to leak 128 or 64 bits.
Fix this by initializing the temporary value before copying the regset
from userspace, as for other regsets (e.g. NT_PRSTATUS, NT_PRFPREG,
NT_ARM_SYSTEM_CALL). In the case of a zero-length or partial write, the
existing contents of the fields which are not written to will be
retained.
To ensure that the extraction and insertion of fields is consistent
across the GETREGSET and SETREGSET calls, new task_gcs_to_user() and
task_gcs_from_user() helpers are added, matching the style of
pac_address_keys_to_user() and pac_address_keys_from_user().
Before this patch:
| # ./gcs-test
| Attempting to write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
| SETREGSET(nt=0x410, len=24) wrote 24 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
|
| Attempting partial write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x1de7ec7edbadc0de,
| .gcspr_el0 = 0x1de7ec7edbadc0de,
| }
| SETREGSET(nt=0x410, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x000000000093e780,
| .gcspr_el0 = 0xffff800083a63d50,
| }
After this patch:
| # ./gcs-test
| Attempting to write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
| SETREGSET(nt=0x410, len=24) wrote 24 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
|
| Attempting partial write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x1de7ec7edbadc0de,
| .gcspr_el0 = 0x1de7ec7edbadc0de,
| }
| SETREGSET(nt=0x410, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
Fixes: 7ec3b57cb29f ("arm64/ptrace: Expose GCS via ptrace and core files")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241205121655.1824269-5-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2024-12-05 12:16:55 +00:00
|
|
|
task_gcs_to_user(&user_gcs, target);
|
2024-10-01 23:59:06 +01:00
|
|
|
|
|
|
|
return membuf_write(&to, &user_gcs, sizeof(user_gcs));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gcs_set(struct task_struct *target, const struct
|
|
|
|
user_regset *regset, unsigned int pos,
|
|
|
|
unsigned int count, const void *kbuf, const
|
|
|
|
void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct user_gcs user_gcs;
|
|
|
|
|
|
|
|
if (!system_supports_gcs())
|
|
|
|
return -EINVAL;
|
|
|
|
|
arm64: ptrace: fix partial SETREGSET for NT_ARM_GCS
Currently gcs_set() doesn't initialize the temporary 'user_gcs'
variable, and a SETREGSET call with a length of 0, 8, or 16 will leave
some portion of this uninitialized. Consequently some arbitrary
uninitialized values may be written back to the relevant fields in task
struct, potentially leaking up to 192 bits of memory from the kernel
stack. The read is limited to a specific slot on the stack, and the
issue does not provide a write mechanism.
As gcs_set() rejects cases where user_gcs::features_enabled has bits set
other than PR_SHADOW_STACK_SUPPORTED_STATUS_MASK, a SETREGSET call with
a length of zero will randomly succeed or fail depending on the value of
the uninitialized value, it isn't possible to leak the full 192 bits.
With a length of 8 or 16, user_gcs::features_enabled can be initialized
to an accepted value, making it practical to leak 128 or 64 bits.
Fix this by initializing the temporary value before copying the regset
from userspace, as for other regsets (e.g. NT_PRSTATUS, NT_PRFPREG,
NT_ARM_SYSTEM_CALL). In the case of a zero-length or partial write, the
existing contents of the fields which are not written to will be
retained.
To ensure that the extraction and insertion of fields is consistent
across the GETREGSET and SETREGSET calls, new task_gcs_to_user() and
task_gcs_from_user() helpers are added, matching the style of
pac_address_keys_to_user() and pac_address_keys_from_user().
Before this patch:
| # ./gcs-test
| Attempting to write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
| SETREGSET(nt=0x410, len=24) wrote 24 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
|
| Attempting partial write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x1de7ec7edbadc0de,
| .gcspr_el0 = 0x1de7ec7edbadc0de,
| }
| SETREGSET(nt=0x410, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x000000000093e780,
| .gcspr_el0 = 0xffff800083a63d50,
| }
After this patch:
| # ./gcs-test
| Attempting to write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
| SETREGSET(nt=0x410, len=24) wrote 24 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
|
| Attempting partial write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x1de7ec7edbadc0de,
| .gcspr_el0 = 0x1de7ec7edbadc0de,
| }
| SETREGSET(nt=0x410, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
Fixes: 7ec3b57cb29f ("arm64/ptrace: Expose GCS via ptrace and core files")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241205121655.1824269-5-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2024-12-05 12:16:55 +00:00
|
|
|
task_gcs_to_user(&user_gcs, target);
|
|
|
|
|
2024-10-01 23:59:06 +01:00
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &user_gcs, 0, -1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (user_gcs.features_enabled & ~PR_SHADOW_STACK_SUPPORTED_STATUS_MASK)
|
|
|
|
return -EINVAL;
|
|
|
|
|
arm64: ptrace: fix partial SETREGSET for NT_ARM_GCS
Currently gcs_set() doesn't initialize the temporary 'user_gcs'
variable, and a SETREGSET call with a length of 0, 8, or 16 will leave
some portion of this uninitialized. Consequently some arbitrary
uninitialized values may be written back to the relevant fields in task
struct, potentially leaking up to 192 bits of memory from the kernel
stack. The read is limited to a specific slot on the stack, and the
issue does not provide a write mechanism.
As gcs_set() rejects cases where user_gcs::features_enabled has bits set
other than PR_SHADOW_STACK_SUPPORTED_STATUS_MASK, a SETREGSET call with
a length of zero will randomly succeed or fail depending on the value of
the uninitialized value, it isn't possible to leak the full 192 bits.
With a length of 8 or 16, user_gcs::features_enabled can be initialized
to an accepted value, making it practical to leak 128 or 64 bits.
Fix this by initializing the temporary value before copying the regset
from userspace, as for other regsets (e.g. NT_PRSTATUS, NT_PRFPREG,
NT_ARM_SYSTEM_CALL). In the case of a zero-length or partial write, the
existing contents of the fields which are not written to will be
retained.
To ensure that the extraction and insertion of fields is consistent
across the GETREGSET and SETREGSET calls, new task_gcs_to_user() and
task_gcs_from_user() helpers are added, matching the style of
pac_address_keys_to_user() and pac_address_keys_from_user().
Before this patch:
| # ./gcs-test
| Attempting to write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
| SETREGSET(nt=0x410, len=24) wrote 24 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
|
| Attempting partial write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x1de7ec7edbadc0de,
| .gcspr_el0 = 0x1de7ec7edbadc0de,
| }
| SETREGSET(nt=0x410, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x000000000093e780,
| .gcspr_el0 = 0xffff800083a63d50,
| }
After this patch:
| # ./gcs-test
| Attempting to write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
| SETREGSET(nt=0x410, len=24) wrote 24 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
|
| Attempting partial write NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x1de7ec7edbadc0de,
| .gcspr_el0 = 0x1de7ec7edbadc0de,
| }
| SETREGSET(nt=0x410, len=8) wrote 8 bytes
|
| Attempting to read NT_ARM_GCS::user_gcs
| GETREGSET(nt=0x410, len=24) read 24 bytes
| Read NT_ARM_GCS::user_gcs = {
| .features_enabled = 0x0000000000000000,
| .features_locked = 0x0000000000000000,
| .gcspr_el0 = 0x900d900d900d900d,
| }
Fixes: 7ec3b57cb29f ("arm64/ptrace: Expose GCS via ptrace and core files")
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20241205121655.1824269-5-mark.rutland@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
2024-12-05 12:16:55 +00:00
|
|
|
task_gcs_from_user(target, &user_gcs);
|
2024-10-01 23:59:06 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
enum aarch64_regset {
|
|
|
|
REGSET_GPR,
|
|
|
|
REGSET_FPR,
|
|
|
|
REGSET_TLS,
|
|
|
|
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
|
|
|
REGSET_HW_BREAK,
|
|
|
|
REGSET_HW_WATCH,
|
|
|
|
#endif
|
2024-03-06 23:14:50 +00:00
|
|
|
REGSET_FPMR,
|
2014-11-28 05:26:34 +00:00
|
|
|
REGSET_SYSTEM_CALL,
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
#ifdef CONFIG_ARM64_SVE
|
|
|
|
REGSET_SVE,
|
|
|
|
#endif
|
2022-12-14 21:59:43 +08:00
|
|
|
#ifdef CONFIG_ARM64_SME
|
2022-04-19 12:22:28 +01:00
|
|
|
REGSET_SSVE,
|
2022-04-19 12:22:29 +01:00
|
|
|
REGSET_ZA,
|
2023-01-16 16:04:47 +00:00
|
|
|
REGSET_ZT,
|
2022-04-19 12:22:28 +01:00
|
|
|
#endif
|
2018-12-07 18:39:26 +00:00
|
|
|
#ifdef CONFIG_ARM64_PTR_AUTH
|
|
|
|
REGSET_PAC_MASK,
|
2021-03-18 20:10:53 -07:00
|
|
|
REGSET_PAC_ENABLED_KEYS,
|
2019-01-30 12:02:44 +00:00
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
|
|
|
REGSET_PACA_KEYS,
|
|
|
|
REGSET_PACG_KEYS,
|
|
|
|
#endif
|
2018-12-07 18:39:26 +00:00
|
|
|
#endif
|
2020-07-03 15:12:57 +01:00
|
|
|
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
|
|
|
|
REGSET_TAGGED_ADDR_CTRL,
|
|
|
|
#endif
|
2024-08-22 16:11:03 +01:00
|
|
|
#ifdef CONFIG_ARM64_POE
|
2024-10-01 23:59:06 +01:00
|
|
|
REGSET_POE,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARM64_GCS
|
|
|
|
REGSET_GCS,
|
2024-08-22 16:11:03 +01:00
|
|
|
#endif
|
2012-03-05 11:49:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct user_regset aarch64_regsets[] = {
|
|
|
|
[REGSET_GPR] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(PRSTATUS),
|
2012-03-05 11:49:33 +00:00
|
|
|
.n = sizeof(struct user_pt_regs) / sizeof(u64),
|
|
|
|
.size = sizeof(u64),
|
|
|
|
.align = sizeof(u64),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = gpr_get,
|
2012-03-05 11:49:33 +00:00
|
|
|
.set = gpr_set
|
|
|
|
},
|
|
|
|
[REGSET_FPR] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(PRFPREG),
|
2012-03-05 11:49:33 +00:00
|
|
|
.n = sizeof(struct user_fpsimd_state) / sizeof(u32),
|
|
|
|
/*
|
|
|
|
* We pretend we have 32-bit registers because the fpsr and
|
|
|
|
* fpcr are 32-bits wide.
|
|
|
|
*/
|
|
|
|
.size = sizeof(u32),
|
|
|
|
.align = sizeof(u32),
|
2020-01-13 23:30:21 +00:00
|
|
|
.active = fpr_active,
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = fpr_get,
|
2012-03-05 11:49:33 +00:00
|
|
|
.set = fpr_set
|
|
|
|
},
|
|
|
|
[REGSET_TLS] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_TLS),
|
2022-08-29 16:49:20 +01:00
|
|
|
.n = 2,
|
2012-03-05 11:49:33 +00:00
|
|
|
.size = sizeof(void *),
|
|
|
|
.align = sizeof(void *),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = tls_get,
|
2012-03-05 11:49:33 +00:00
|
|
|
.set = tls_set,
|
|
|
|
},
|
|
|
|
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
|
|
|
[REGSET_HW_BREAK] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_HW_BREAK),
|
2012-03-05 11:49:33 +00:00
|
|
|
.n = sizeof(struct user_hwdebug_state) / sizeof(u32),
|
|
|
|
.size = sizeof(u32),
|
|
|
|
.align = sizeof(u32),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = hw_break_get,
|
2012-03-05 11:49:33 +00:00
|
|
|
.set = hw_break_set,
|
|
|
|
},
|
|
|
|
[REGSET_HW_WATCH] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_HW_WATCH),
|
2012-03-05 11:49:33 +00:00
|
|
|
.n = sizeof(struct user_hwdebug_state) / sizeof(u32),
|
|
|
|
.size = sizeof(u32),
|
|
|
|
.align = sizeof(u32),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = hw_break_get,
|
2012-03-05 11:49:33 +00:00
|
|
|
.set = hw_break_set,
|
|
|
|
},
|
|
|
|
#endif
|
2014-11-28 05:26:34 +00:00
|
|
|
[REGSET_SYSTEM_CALL] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_SYSTEM_CALL),
|
2014-11-28 05:26:34 +00:00
|
|
|
.n = 1,
|
|
|
|
.size = sizeof(int),
|
|
|
|
.align = sizeof(int),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = system_call_get,
|
2014-11-28 05:26:34 +00:00
|
|
|
.set = system_call_set,
|
|
|
|
},
|
2024-03-06 23:14:50 +00:00
|
|
|
[REGSET_FPMR] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_FPMR),
|
2024-03-06 23:14:50 +00:00
|
|
|
.n = 1,
|
|
|
|
.size = sizeof(u64),
|
|
|
|
.align = sizeof(u64),
|
|
|
|
.regset_get = fpmr_get,
|
|
|
|
.set = fpmr_set,
|
|
|
|
},
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
#ifdef CONFIG_ARM64_SVE
|
|
|
|
[REGSET_SVE] = { /* Scalable Vector Extension */
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_SVE),
|
2024-02-13 18:24:38 +00:00
|
|
|
.n = DIV_ROUND_UP(SVE_PT_SIZE(ARCH_SVE_VQ_MAX,
|
|
|
|
SVE_PT_REGS_SVE),
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
SVE_VQ_BYTES),
|
|
|
|
.size = SVE_VQ_BYTES,
|
|
|
|
.align = SVE_VQ_BYTES,
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = sve_get,
|
arm64/sve: ptrace and ELF coredump support
This patch defines and implements a new regset NT_ARM_SVE, which
describes a thread's SVE register state. This allows a debugger to
manipulate the SVE state, as well as being included in ELF
coredumps for post-mortem debugging.
Because the regset size and layout are dependent on the thread's
current vector length, it is not possible to define a C struct to
describe the regset contents as is done for existing regsets.
Instead, and for the same reasons, NT_ARM_SVE is based on the
freeform variable-layout approach used for the SVE signal frame.
Additionally, to reduce debug overhead when debugging threads that
might or might not have live SVE register state, NT_ARM_SVE may be
presented in one of two different formats: the old struct
user_fpsimd_state format is embedded for describing the state of a
thread with no live SVE state, whereas a new variable-layout
structure is embedded for describing live SVE state. This avoids a
debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and
allows existing userspace code to handle the non-SVE case without
too much modification.
For this to work, NT_ARM_SVE is defined with a fixed-format header
of type struct user_sve_header, which the recipient can use to
figure out the content, size and layout of the reset of the regset.
Accessor macros are defined to allow the vector-length-dependent
parts of the regset to be manipulated.
Signed-off-by: Alan Hayward <alan.hayward@arm.com>
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Alex Bennée <alex.bennee@linaro.org>
Cc: Okamoto Takayuki <tokamoto@jp.fujitsu.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2017-10-31 15:51:13 +00:00
|
|
|
.set = sve_set,
|
|
|
|
},
|
|
|
|
#endif
|
2022-04-19 12:22:28 +01:00
|
|
|
#ifdef CONFIG_ARM64_SME
|
|
|
|
[REGSET_SSVE] = { /* Streaming mode SVE */
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_SSVE),
|
2022-05-05 23:15:17 +01:00
|
|
|
.n = DIV_ROUND_UP(SVE_PT_SIZE(SME_VQ_MAX, SVE_PT_REGS_SVE),
|
2022-04-19 12:22:28 +01:00
|
|
|
SVE_VQ_BYTES),
|
|
|
|
.size = SVE_VQ_BYTES,
|
|
|
|
.align = SVE_VQ_BYTES,
|
|
|
|
.regset_get = ssve_get,
|
|
|
|
.set = ssve_set,
|
|
|
|
},
|
2022-04-19 12:22:29 +01:00
|
|
|
[REGSET_ZA] = { /* SME ZA */
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_ZA),
|
2022-05-05 23:15:17 +01:00
|
|
|
/*
|
|
|
|
* ZA is a single register but it's variably sized and
|
|
|
|
* the ptrace core requires that the size of any data
|
|
|
|
* be an exact multiple of the configured register
|
|
|
|
* size so report as though we had SVE_VQ_BYTES
|
|
|
|
* registers. These values aren't exposed to
|
|
|
|
* userspace.
|
|
|
|
*/
|
|
|
|
.n = DIV_ROUND_UP(ZA_PT_SIZE(SME_VQ_MAX), SVE_VQ_BYTES),
|
2022-04-19 12:22:29 +01:00
|
|
|
.size = SVE_VQ_BYTES,
|
|
|
|
.align = SVE_VQ_BYTES,
|
|
|
|
.regset_get = za_get,
|
|
|
|
.set = za_set,
|
|
|
|
},
|
2023-01-16 16:04:47 +00:00
|
|
|
[REGSET_ZT] = { /* SME ZT */
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_ZT),
|
2023-01-16 16:04:47 +00:00
|
|
|
.n = 1,
|
|
|
|
.size = ZT_SIG_REG_BYTES,
|
|
|
|
.align = sizeof(u64),
|
|
|
|
.regset_get = zt_get,
|
|
|
|
.set = zt_set,
|
|
|
|
},
|
2022-04-19 12:22:28 +01:00
|
|
|
#endif
|
2018-12-07 18:39:26 +00:00
|
|
|
#ifdef CONFIG_ARM64_PTR_AUTH
|
|
|
|
[REGSET_PAC_MASK] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_PAC_MASK),
|
2018-12-07 18:39:26 +00:00
|
|
|
.n = sizeof(struct user_pac_mask) / sizeof(u64),
|
|
|
|
.size = sizeof(u64),
|
|
|
|
.align = sizeof(u64),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = pac_mask_get,
|
2018-12-07 18:39:26 +00:00
|
|
|
/* this cannot be set dynamically */
|
|
|
|
},
|
2021-03-18 20:10:53 -07:00
|
|
|
[REGSET_PAC_ENABLED_KEYS] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_PAC_ENABLED_KEYS),
|
2021-03-18 20:10:53 -07:00
|
|
|
.n = 1,
|
|
|
|
.size = sizeof(long),
|
|
|
|
.align = sizeof(long),
|
|
|
|
.regset_get = pac_enabled_keys_get,
|
|
|
|
.set = pac_enabled_keys_set,
|
|
|
|
},
|
2019-01-30 12:02:44 +00:00
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
|
|
|
[REGSET_PACA_KEYS] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_PACA_KEYS),
|
2019-01-30 12:02:44 +00:00
|
|
|
.n = sizeof(struct user_pac_address_keys) / sizeof(__uint128_t),
|
|
|
|
.size = sizeof(__uint128_t),
|
|
|
|
.align = sizeof(__uint128_t),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = pac_address_keys_get,
|
2019-01-30 12:02:44 +00:00
|
|
|
.set = pac_address_keys_set,
|
|
|
|
},
|
|
|
|
[REGSET_PACG_KEYS] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_PACG_KEYS),
|
2019-01-30 12:02:44 +00:00
|
|
|
.n = sizeof(struct user_pac_generic_keys) / sizeof(__uint128_t),
|
|
|
|
.size = sizeof(__uint128_t),
|
|
|
|
.align = sizeof(__uint128_t),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = pac_generic_keys_get,
|
2019-01-30 12:02:44 +00:00
|
|
|
.set = pac_generic_keys_set,
|
|
|
|
},
|
|
|
|
#endif
|
2018-12-07 18:39:26 +00:00
|
|
|
#endif
|
2020-07-03 15:12:57 +01:00
|
|
|
#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
|
|
|
|
[REGSET_TAGGED_ADDR_CTRL] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_TAGGED_ADDR_CTRL),
|
2020-07-03 15:12:57 +01:00
|
|
|
.n = 1,
|
|
|
|
.size = sizeof(long),
|
|
|
|
.align = sizeof(long),
|
|
|
|
.regset_get = tagged_addr_ctrl_get,
|
|
|
|
.set = tagged_addr_ctrl_set,
|
|
|
|
},
|
|
|
|
#endif
|
2024-08-22 16:11:03 +01:00
|
|
|
#ifdef CONFIG_ARM64_POE
|
|
|
|
[REGSET_POE] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_POE),
|
2024-08-22 16:11:03 +01:00
|
|
|
.n = 1,
|
|
|
|
.size = sizeof(long),
|
|
|
|
.align = sizeof(long),
|
|
|
|
.regset_get = poe_get,
|
|
|
|
.set = poe_set,
|
|
|
|
},
|
|
|
|
#endif
|
2024-10-01 23:59:06 +01:00
|
|
|
#ifdef CONFIG_ARM64_GCS
|
|
|
|
[REGSET_GCS] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_GCS),
|
2024-10-01 23:59:06 +01:00
|
|
|
.n = sizeof(struct user_gcs) / sizeof(u64),
|
|
|
|
.size = sizeof(u64),
|
|
|
|
.align = sizeof(u64),
|
|
|
|
.regset_get = gcs_get,
|
|
|
|
.set = gcs_set,
|
|
|
|
},
|
|
|
|
#endif
|
2012-03-05 11:49:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct user_regset_view user_aarch64_view = {
|
|
|
|
.name = "aarch64", .e_machine = EM_AARCH64,
|
|
|
|
.regsets = aarch64_regsets, .n = ARRAY_SIZE(aarch64_regsets)
|
|
|
|
};
|
|
|
|
|
|
|
|
enum compat_regset {
|
|
|
|
REGSET_COMPAT_GPR,
|
|
|
|
REGSET_COMPAT_VFP,
|
|
|
|
};
|
|
|
|
|
2020-05-15 11:22:26 -04:00
|
|
|
static inline compat_ulong_t compat_get_user_reg(struct task_struct *task, int idx)
|
|
|
|
{
|
|
|
|
struct pt_regs *regs = task_pt_regs(task);
|
|
|
|
|
|
|
|
switch (idx) {
|
|
|
|
case 15:
|
|
|
|
return regs->pc;
|
|
|
|
case 16:
|
|
|
|
return pstate_to_compat_psr(regs->pstate);
|
|
|
|
case 17:
|
|
|
|
return regs->orig_x0;
|
|
|
|
default:
|
|
|
|
return regs->regs[idx];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
static int compat_gpr_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2012-03-05 11:49:33 +00:00
|
|
|
{
|
2020-05-27 19:09:44 -04:00
|
|
|
int i = 0;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
2020-05-27 19:09:44 -04:00
|
|
|
while (to.left)
|
|
|
|
membuf_store(&to, compat_get_user_reg(target, i++));
|
|
|
|
return 0;
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_gpr_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
struct pt_regs newregs;
|
|
|
|
int ret = 0;
|
|
|
|
unsigned int i, start, num_regs;
|
|
|
|
|
|
|
|
/* Calculate the number of AArch32 registers contained in count */
|
|
|
|
num_regs = count / regset->size;
|
|
|
|
|
|
|
|
/* Convert pos into an register number */
|
|
|
|
start = pos / regset->size;
|
|
|
|
|
|
|
|
if (start + num_regs > regset->n)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
newregs = *task_pt_regs(target);
|
|
|
|
|
|
|
|
for (i = 0; i < num_regs; ++i) {
|
|
|
|
unsigned int idx = start + i;
|
2013-11-28 12:07:22 +00:00
|
|
|
compat_ulong_t reg;
|
|
|
|
|
2014-06-03 19:21:30 +01:00
|
|
|
if (kbuf) {
|
|
|
|
memcpy(®, kbuf, sizeof(reg));
|
|
|
|
kbuf += sizeof(reg);
|
|
|
|
} else {
|
|
|
|
ret = copy_from_user(®, ubuf, sizeof(reg));
|
2014-08-22 14:20:24 +01:00
|
|
|
if (ret) {
|
|
|
|
ret = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
2013-11-28 12:07:22 +00:00
|
|
|
|
2014-06-03 19:21:30 +01:00
|
|
|
ubuf += sizeof(reg);
|
|
|
|
}
|
2012-03-05 11:49:33 +00:00
|
|
|
|
|
|
|
switch (idx) {
|
|
|
|
case 15:
|
2013-11-28 12:07:22 +00:00
|
|
|
newregs.pc = reg;
|
2012-03-05 11:49:33 +00:00
|
|
|
break;
|
|
|
|
case 16:
|
2018-07-05 15:16:51 +01:00
|
|
|
reg = compat_psr_to_pstate(reg);
|
2013-11-28 12:07:22 +00:00
|
|
|
newregs.pstate = reg;
|
2012-03-05 11:49:33 +00:00
|
|
|
break;
|
|
|
|
case 17:
|
2013-11-28 12:07:22 +00:00
|
|
|
newregs.orig_x0 = reg;
|
2012-03-05 11:49:33 +00:00
|
|
|
break;
|
|
|
|
default:
|
2013-11-28 12:07:22 +00:00
|
|
|
newregs.regs[idx] = reg;
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-03-01 14:18:50 +00:00
|
|
|
if (valid_user_regs(&newregs.user_regs, target))
|
2012-03-05 11:49:33 +00:00
|
|
|
*task_pt_regs(target) = newregs;
|
|
|
|
else
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_vfp_get(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
2020-05-27 19:09:44 -04:00
|
|
|
struct membuf to)
|
2012-03-05 11:49:33 +00:00
|
|
|
{
|
|
|
|
struct user_fpsimd_state *uregs;
|
|
|
|
compat_ulong_t fpscr;
|
|
|
|
|
2020-01-13 23:30:21 +00:00
|
|
|
if (!system_supports_fpsimd())
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-03-28 10:50:49 +01:00
|
|
|
uregs = &target->thread.uw.fpsimd_state;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
2017-06-21 16:00:43 +01:00
|
|
|
if (target == current)
|
|
|
|
fpsimd_preserve_current_state();
|
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
/*
|
|
|
|
* The VFP registers are packed into the fpsimd_state, so they all sit
|
|
|
|
* nicely together for us. We just need to create the fpscr separately.
|
|
|
|
*/
|
2020-05-27 19:09:44 -04:00
|
|
|
membuf_write(&to, uregs, VFP_STATE_SIZE - sizeof(compat_ulong_t));
|
|
|
|
fpscr = (uregs->fpsr & VFP_FPSCR_STAT_MASK) |
|
|
|
|
(uregs->fpcr & VFP_FPSCR_CTRL_MASK);
|
|
|
|
return membuf_store(&to, fpscr);
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_vfp_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset,
|
|
|
|
unsigned int pos, unsigned int count,
|
|
|
|
const void *kbuf, const void __user *ubuf)
|
|
|
|
{
|
|
|
|
struct user_fpsimd_state *uregs;
|
|
|
|
compat_ulong_t fpscr;
|
2017-06-29 15:25:49 +01:00
|
|
|
int ret, vregs_end_pos;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
2020-01-13 23:30:21 +00:00
|
|
|
if (!system_supports_fpsimd())
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-03-28 10:50:49 +01:00
|
|
|
uregs = &target->thread.uw.fpsimd_state;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
2017-06-29 15:25:49 +01:00
|
|
|
vregs_end_pos = VFP_STATE_SIZE - sizeof(compat_ulong_t);
|
2012-03-05 11:49:33 +00:00
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, uregs, 0,
|
2017-06-29 15:25:49 +01:00
|
|
|
vregs_end_pos);
|
2012-03-05 11:49:33 +00:00
|
|
|
|
|
|
|
if (count && !ret) {
|
2017-06-29 15:25:49 +01:00
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpscr,
|
|
|
|
vregs_end_pos, VFP_STATE_SIZE);
|
2017-06-29 15:25:47 +01:00
|
|
|
if (!ret) {
|
|
|
|
uregs->fpsr = fpscr & VFP_FPSCR_STAT_MASK;
|
|
|
|
uregs->fpcr = fpscr & VFP_FPSCR_CTRL_MASK;
|
|
|
|
}
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
2014-05-08 11:20:23 +02:00
|
|
|
fpsimd_flush_task_state(target);
|
2012-03-05 11:49:33 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-07-14 16:20:17 +01:00
|
|
|
static int compat_tls_get(struct task_struct *target,
|
2020-05-27 19:09:44 -04:00
|
|
|
const struct user_regset *regset,
|
|
|
|
struct membuf to)
|
2015-07-14 16:20:17 +01:00
|
|
|
{
|
2020-05-27 19:09:44 -04:00
|
|
|
return membuf_store(&to, (compat_ulong_t)target->thread.uw.tp_value);
|
2015-07-14 16:20:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_tls_set(struct task_struct *target,
|
|
|
|
const struct user_regset *regset, unsigned int pos,
|
|
|
|
unsigned int count, const void *kbuf,
|
|
|
|
const void __user *ubuf)
|
|
|
|
{
|
|
|
|
int ret;
|
2018-03-28 10:50:49 +01:00
|
|
|
compat_ulong_t tls = target->thread.uw.tp_value;
|
2015-07-14 16:20:17 +01:00
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2018-03-28 10:50:49 +01:00
|
|
|
target->thread.uw.tp_value = tls;
|
2015-07-14 16:20:17 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
static const struct user_regset aarch32_regsets[] = {
|
|
|
|
[REGSET_COMPAT_GPR] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(PRSTATUS),
|
2012-03-05 11:49:33 +00:00
|
|
|
.n = COMPAT_ELF_NGREG,
|
|
|
|
.size = sizeof(compat_elf_greg_t),
|
|
|
|
.align = sizeof(compat_elf_greg_t),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = compat_gpr_get,
|
2012-03-05 11:49:33 +00:00
|
|
|
.set = compat_gpr_set
|
|
|
|
},
|
|
|
|
[REGSET_COMPAT_VFP] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_VFP),
|
2012-03-05 11:49:33 +00:00
|
|
|
.n = VFP_STATE_SIZE / sizeof(compat_ulong_t),
|
|
|
|
.size = sizeof(compat_ulong_t),
|
|
|
|
.align = sizeof(compat_ulong_t),
|
2020-01-13 23:30:21 +00:00
|
|
|
.active = fpr_active,
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = compat_vfp_get,
|
2012-03-05 11:49:33 +00:00
|
|
|
.set = compat_vfp_set
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct user_regset_view user_aarch32_view = {
|
|
|
|
.name = "aarch32", .e_machine = EM_ARM,
|
|
|
|
.regsets = aarch32_regsets, .n = ARRAY_SIZE(aarch32_regsets)
|
|
|
|
};
|
|
|
|
|
2015-07-14 16:20:17 +01:00
|
|
|
static const struct user_regset aarch32_ptrace_regsets[] = {
|
|
|
|
[REGSET_GPR] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(PRSTATUS),
|
2015-07-14 16:20:17 +01:00
|
|
|
.n = COMPAT_ELF_NGREG,
|
|
|
|
.size = sizeof(compat_elf_greg_t),
|
|
|
|
.align = sizeof(compat_elf_greg_t),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = compat_gpr_get,
|
2015-07-14 16:20:17 +01:00
|
|
|
.set = compat_gpr_set
|
|
|
|
},
|
|
|
|
[REGSET_FPR] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_VFP),
|
2015-07-14 16:20:17 +01:00
|
|
|
.n = VFP_STATE_SIZE / sizeof(compat_ulong_t),
|
|
|
|
.size = sizeof(compat_ulong_t),
|
|
|
|
.align = sizeof(compat_ulong_t),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = compat_vfp_get,
|
2015-07-14 16:20:17 +01:00
|
|
|
.set = compat_vfp_set
|
|
|
|
},
|
|
|
|
[REGSET_TLS] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_TLS),
|
2015-07-14 16:20:17 +01:00
|
|
|
.n = 1,
|
|
|
|
.size = sizeof(compat_ulong_t),
|
|
|
|
.align = sizeof(compat_ulong_t),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = compat_tls_get,
|
2015-07-14 16:20:17 +01:00
|
|
|
.set = compat_tls_set,
|
|
|
|
},
|
|
|
|
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
|
|
|
[REGSET_HW_BREAK] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_HW_BREAK),
|
2015-07-14 16:20:17 +01:00
|
|
|
.n = sizeof(struct user_hwdebug_state) / sizeof(u32),
|
|
|
|
.size = sizeof(u32),
|
|
|
|
.align = sizeof(u32),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = hw_break_get,
|
2015-07-14 16:20:17 +01:00
|
|
|
.set = hw_break_set,
|
|
|
|
},
|
|
|
|
[REGSET_HW_WATCH] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_HW_WATCH),
|
2015-07-14 16:20:17 +01:00
|
|
|
.n = sizeof(struct user_hwdebug_state) / sizeof(u32),
|
|
|
|
.size = sizeof(u32),
|
|
|
|
.align = sizeof(u32),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = hw_break_get,
|
2015-07-14 16:20:17 +01:00
|
|
|
.set = hw_break_set,
|
|
|
|
},
|
|
|
|
#endif
|
|
|
|
[REGSET_SYSTEM_CALL] = {
|
2025-07-01 14:55:59 +01:00
|
|
|
USER_REGSET_NOTE_TYPE(ARM_SYSTEM_CALL),
|
2015-07-14 16:20:17 +01:00
|
|
|
.n = 1,
|
|
|
|
.size = sizeof(int),
|
|
|
|
.align = sizeof(int),
|
2020-05-27 19:09:44 -04:00
|
|
|
.regset_get = system_call_get,
|
2015-07-14 16:20:17 +01:00
|
|
|
.set = system_call_set,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct user_regset_view user_aarch32_ptrace_view = {
|
|
|
|
.name = "aarch32", .e_machine = EM_ARM,
|
|
|
|
.regsets = aarch32_ptrace_regsets, .n = ARRAY_SIZE(aarch32_ptrace_regsets)
|
|
|
|
};
|
|
|
|
|
2024-01-09 00:46:50 -03:00
|
|
|
#ifdef CONFIG_COMPAT
|
2012-03-05 11:49:33 +00:00
|
|
|
static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off,
|
|
|
|
compat_ulong_t __user *ret)
|
|
|
|
{
|
|
|
|
compat_ulong_t tmp;
|
|
|
|
|
|
|
|
if (off & 3)
|
|
|
|
return -EIO;
|
|
|
|
|
2012-10-10 15:50:03 +01:00
|
|
|
if (off == COMPAT_PT_TEXT_ADDR)
|
2012-03-05 11:49:33 +00:00
|
|
|
tmp = tsk->mm->start_code;
|
2012-10-10 15:50:03 +01:00
|
|
|
else if (off == COMPAT_PT_DATA_ADDR)
|
2012-03-05 11:49:33 +00:00
|
|
|
tmp = tsk->mm->start_data;
|
2012-10-10 15:50:03 +01:00
|
|
|
else if (off == COMPAT_PT_TEXT_END_ADDR)
|
2012-03-05 11:49:33 +00:00
|
|
|
tmp = tsk->mm->end_code;
|
|
|
|
else if (off < sizeof(compat_elf_gregset_t))
|
2020-05-15 11:26:08 -04:00
|
|
|
tmp = compat_get_user_reg(tsk, off >> 2);
|
2012-03-05 11:49:33 +00:00
|
|
|
else if (off >= COMPAT_USER_SZ)
|
|
|
|
return -EIO;
|
|
|
|
else
|
|
|
|
tmp = 0;
|
|
|
|
|
|
|
|
return put_user(tmp, ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off,
|
|
|
|
compat_ulong_t val)
|
|
|
|
{
|
2020-06-06 21:19:58 -04:00
|
|
|
struct pt_regs newregs = *task_pt_regs(tsk);
|
|
|
|
unsigned int idx = off / 4;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
|
|
|
if (off & 3 || off >= COMPAT_USER_SZ)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (off >= sizeof(compat_elf_gregset_t))
|
|
|
|
return 0;
|
|
|
|
|
2020-06-06 21:19:58 -04:00
|
|
|
switch (idx) {
|
|
|
|
case 15:
|
|
|
|
newregs.pc = val;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
newregs.pstate = compat_psr_to_pstate(val);
|
|
|
|
break;
|
|
|
|
case 17:
|
|
|
|
newregs.orig_x0 = val;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
newregs.regs[idx] = val;
|
|
|
|
}
|
2014-06-02 11:47:23 +01:00
|
|
|
|
2020-06-06 21:19:58 -04:00
|
|
|
if (!valid_user_regs(&newregs.user_regs, tsk))
|
|
|
|
return -EINVAL;
|
2014-06-02 11:47:23 +01:00
|
|
|
|
2020-06-06 21:19:58 -04:00
|
|
|
*task_pt_regs(tsk) = newregs;
|
|
|
|
return 0;
|
2012-03-05 11:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert a virtual register number into an index for a thread_info
|
|
|
|
* breakpoint array. Breakpoints are identified using positive numbers
|
|
|
|
* whilst watchpoints are negative. The registers are laid out as pairs
|
|
|
|
* of (address, control), each pair mapping to a unique hw_breakpoint struct.
|
|
|
|
* Register 0 is reserved for describing resource information.
|
|
|
|
*/
|
|
|
|
static int compat_ptrace_hbp_num_to_idx(compat_long_t num)
|
|
|
|
{
|
|
|
|
return (abs(num) - 1) >> 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_ptrace_hbp_get_resource_info(u32 *kdata)
|
|
|
|
{
|
|
|
|
u8 num_brps, num_wrps, debug_arch, wp_len;
|
|
|
|
u32 reg = 0;
|
|
|
|
|
|
|
|
num_brps = hw_breakpoint_slots(TYPE_INST);
|
|
|
|
num_wrps = hw_breakpoint_slots(TYPE_DATA);
|
|
|
|
|
|
|
|
debug_arch = debug_monitors_arch();
|
|
|
|
wp_len = 8;
|
|
|
|
reg |= debug_arch;
|
|
|
|
reg <<= 8;
|
|
|
|
reg |= wp_len;
|
|
|
|
reg <<= 8;
|
|
|
|
reg |= num_wrps;
|
|
|
|
reg <<= 8;
|
|
|
|
reg |= num_brps;
|
|
|
|
|
|
|
|
*kdata = reg;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_ptrace_hbp_get(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
compat_long_t num,
|
|
|
|
u32 *kdata)
|
|
|
|
{
|
|
|
|
u64 addr = 0;
|
|
|
|
u32 ctrl = 0;
|
|
|
|
|
2018-02-22 10:54:55 +01:00
|
|
|
int err, idx = compat_ptrace_hbp_num_to_idx(num);
|
2012-03-05 11:49:33 +00:00
|
|
|
|
|
|
|
if (num & 1) {
|
|
|
|
err = ptrace_hbp_get_addr(note_type, tsk, idx, &addr);
|
|
|
|
*kdata = (u32)addr;
|
|
|
|
} else {
|
|
|
|
err = ptrace_hbp_get_ctrl(note_type, tsk, idx, &ctrl);
|
|
|
|
*kdata = ctrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_ptrace_hbp_set(unsigned int note_type,
|
|
|
|
struct task_struct *tsk,
|
|
|
|
compat_long_t num,
|
|
|
|
u32 *kdata)
|
|
|
|
{
|
|
|
|
u64 addr;
|
|
|
|
u32 ctrl;
|
|
|
|
|
|
|
|
int err, idx = compat_ptrace_hbp_num_to_idx(num);
|
|
|
|
|
|
|
|
if (num & 1) {
|
|
|
|
addr = *kdata;
|
|
|
|
err = ptrace_hbp_set_addr(note_type, tsk, idx, addr);
|
|
|
|
} else {
|
|
|
|
ctrl = *kdata;
|
|
|
|
err = ptrace_hbp_set_ctrl(note_type, tsk, idx, ctrl);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_ptrace_gethbpregs(struct task_struct *tsk, compat_long_t num,
|
|
|
|
compat_ulong_t __user *data)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
u32 kdata;
|
|
|
|
|
|
|
|
/* Watchpoint */
|
|
|
|
if (num < 0) {
|
|
|
|
ret = compat_ptrace_hbp_get(NT_ARM_HW_WATCH, tsk, num, &kdata);
|
|
|
|
/* Resource info */
|
|
|
|
} else if (num == 0) {
|
|
|
|
ret = compat_ptrace_hbp_get_resource_info(&kdata);
|
|
|
|
/* Breakpoint */
|
|
|
|
} else {
|
|
|
|
ret = compat_ptrace_hbp_get(NT_ARM_HW_BREAK, tsk, num, &kdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = put_user(kdata, data);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int compat_ptrace_sethbpregs(struct task_struct *tsk, compat_long_t num,
|
|
|
|
compat_ulong_t __user *data)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
u32 kdata = 0;
|
|
|
|
|
|
|
|
if (num == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret = get_user(kdata, data);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (num < 0)
|
|
|
|
ret = compat_ptrace_hbp_set(NT_ARM_HW_WATCH, tsk, num, &kdata);
|
|
|
|
else
|
|
|
|
ret = compat_ptrace_hbp_set(NT_ARM_HW_BREAK, tsk, num, &kdata);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_HAVE_HW_BREAKPOINT */
|
|
|
|
|
|
|
|
long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
|
|
|
compat_ulong_t caddr, compat_ulong_t cdata)
|
|
|
|
{
|
|
|
|
unsigned long addr = caddr;
|
|
|
|
unsigned long data = cdata;
|
|
|
|
void __user *datap = compat_ptr(data);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
switch (request) {
|
|
|
|
case PTRACE_PEEKUSR:
|
|
|
|
ret = compat_ptrace_read_user(child, addr, datap);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PTRACE_POKEUSR:
|
|
|
|
ret = compat_ptrace_write_user(child, addr, data);
|
|
|
|
break;
|
|
|
|
|
2012-09-27 11:38:12 +01:00
|
|
|
case COMPAT_PTRACE_GETREGS:
|
2012-03-05 11:49:33 +00:00
|
|
|
ret = copy_regset_to_user(child,
|
|
|
|
&user_aarch32_view,
|
|
|
|
REGSET_COMPAT_GPR,
|
|
|
|
0, sizeof(compat_elf_gregset_t),
|
|
|
|
datap);
|
|
|
|
break;
|
|
|
|
|
2012-09-27 11:38:12 +01:00
|
|
|
case COMPAT_PTRACE_SETREGS:
|
2012-03-05 11:49:33 +00:00
|
|
|
ret = copy_regset_from_user(child,
|
|
|
|
&user_aarch32_view,
|
|
|
|
REGSET_COMPAT_GPR,
|
|
|
|
0, sizeof(compat_elf_gregset_t),
|
|
|
|
datap);
|
|
|
|
break;
|
|
|
|
|
2012-09-27 11:38:12 +01:00
|
|
|
case COMPAT_PTRACE_GET_THREAD_AREA:
|
2018-03-28 10:50:49 +01:00
|
|
|
ret = put_user((compat_ulong_t)child->thread.uw.tp_value,
|
2012-03-05 11:49:33 +00:00
|
|
|
(compat_ulong_t __user *)datap);
|
|
|
|
break;
|
|
|
|
|
2012-09-27 11:38:12 +01:00
|
|
|
case COMPAT_PTRACE_SET_SYSCALL:
|
2012-03-05 11:49:33 +00:00
|
|
|
task_pt_regs(child)->syscallno = data;
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COMPAT_PTRACE_GETVFPREGS:
|
|
|
|
ret = copy_regset_to_user(child,
|
|
|
|
&user_aarch32_view,
|
|
|
|
REGSET_COMPAT_VFP,
|
|
|
|
0, VFP_STATE_SIZE,
|
|
|
|
datap);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COMPAT_PTRACE_SETVFPREGS:
|
|
|
|
ret = copy_regset_from_user(child,
|
|
|
|
&user_aarch32_view,
|
|
|
|
REGSET_COMPAT_VFP,
|
|
|
|
0, VFP_STATE_SIZE,
|
|
|
|
datap);
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifdef CONFIG_HAVE_HW_BREAKPOINT
|
2012-09-27 11:38:12 +01:00
|
|
|
case COMPAT_PTRACE_GETHBPREGS:
|
2012-03-05 11:49:33 +00:00
|
|
|
ret = compat_ptrace_gethbpregs(child, addr, datap);
|
|
|
|
break;
|
|
|
|
|
2012-09-27 11:38:12 +01:00
|
|
|
case COMPAT_PTRACE_SETHBPREGS:
|
2012-03-05 11:49:33 +00:00
|
|
|
ret = compat_ptrace_sethbpregs(child, addr, datap);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
ret = compat_ptrace_request(child, request, addr,
|
|
|
|
data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_COMPAT */
|
|
|
|
|
|
|
|
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
|
|
|
|
{
|
2015-07-14 16:20:17 +01:00
|
|
|
/*
|
|
|
|
* Core dumping of 32-bit tasks or compat ptrace requests must use the
|
|
|
|
* user_aarch32_view compatible with arm32. Native ptrace requests on
|
|
|
|
* 32-bit children use an extended user_aarch32_ptrace_view to allow
|
|
|
|
* access to the TLS register.
|
|
|
|
*/
|
|
|
|
if (is_compat_task())
|
2012-03-05 11:49:33 +00:00
|
|
|
return &user_aarch32_view;
|
2015-07-14 16:20:17 +01:00
|
|
|
else if (is_compat_thread(task_thread_info(task)))
|
|
|
|
return &user_aarch32_ptrace_view;
|
2024-01-09 00:46:50 -03:00
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
return &user_aarch64_view;
|
|
|
|
}
|
|
|
|
|
|
|
|
long arch_ptrace(struct task_struct *child, long request,
|
|
|
|
unsigned long addr, unsigned long data)
|
|
|
|
{
|
2020-03-30 10:29:38 +01:00
|
|
|
switch (request) {
|
|
|
|
case PTRACE_PEEKMTETAGS:
|
|
|
|
case PTRACE_POKEMTETAGS:
|
|
|
|
return mte_ptrace_copy_tags(child, request, addr, data);
|
|
|
|
}
|
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
return ptrace_request(child, request, addr, data);
|
|
|
|
}
|
|
|
|
|
2014-04-30 10:51:30 +01:00
|
|
|
enum ptrace_syscall_dir {
|
|
|
|
PTRACE_SYSCALL_ENTER = 0,
|
|
|
|
PTRACE_SYSCALL_EXIT,
|
|
|
|
};
|
|
|
|
|
2022-01-27 11:27:10 -06:00
|
|
|
static void report_syscall(struct pt_regs *regs, enum ptrace_syscall_dir dir)
|
2012-03-05 11:49:33 +00:00
|
|
|
{
|
2014-04-30 10:51:30 +01:00
|
|
|
int regno;
|
2012-03-05 11:49:33 +00:00
|
|
|
unsigned long saved_reg;
|
|
|
|
|
2014-04-30 10:51:30 +01:00
|
|
|
/*
|
2020-07-02 21:14:20 +01:00
|
|
|
* We have some ABI weirdness here in the way that we handle syscall
|
|
|
|
* exit stops because we indicate whether or not the stop has been
|
|
|
|
* signalled from syscall entry or syscall exit by clobbering a general
|
|
|
|
* purpose register (ip/r12 for AArch32, x7 for AArch64) in the tracee
|
|
|
|
* and restoring its old value after the stop. This means that:
|
|
|
|
*
|
|
|
|
* - Any writes by the tracer to this register during the stop are
|
|
|
|
* ignored/discarded.
|
|
|
|
*
|
|
|
|
* - The actual value of the register is not available during the stop,
|
|
|
|
* so the tracer cannot save it and restore it later.
|
|
|
|
*
|
|
|
|
* - Syscall stops behave differently to seccomp and pseudo-step traps
|
|
|
|
* (the latter do not nobble any registers).
|
2014-04-30 10:51:30 +01:00
|
|
|
*/
|
|
|
|
regno = (is_compat_task() ? 12 : 7);
|
|
|
|
saved_reg = regs->regs[regno];
|
|
|
|
regs->regs[regno] = dir;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
2020-07-02 21:16:20 +01:00
|
|
|
if (dir == PTRACE_SYSCALL_ENTER) {
|
2022-01-27 11:46:37 -06:00
|
|
|
if (ptrace_report_syscall_entry(regs))
|
2020-07-02 21:16:20 +01:00
|
|
|
forget_syscall(regs);
|
|
|
|
regs->regs[regno] = saved_reg;
|
|
|
|
} else if (!test_thread_flag(TIF_SINGLESTEP)) {
|
2022-01-27 11:46:37 -06:00
|
|
|
ptrace_report_syscall_exit(regs, 0);
|
2020-07-02 21:16:20 +01:00
|
|
|
regs->regs[regno] = saved_reg;
|
|
|
|
} else {
|
|
|
|
regs->regs[regno] = saved_reg;
|
2012-03-05 11:49:33 +00:00
|
|
|
|
2020-07-02 21:16:20 +01:00
|
|
|
/*
|
|
|
|
* Signal a pseudo-step exception since we are stepping but
|
|
|
|
* tracer modifications to the registers may have rewound the
|
|
|
|
* state machine.
|
|
|
|
*/
|
2022-01-27 11:46:37 -06:00
|
|
|
ptrace_report_syscall_exit(regs, 1);
|
2020-07-02 21:16:20 +01:00
|
|
|
}
|
2014-04-30 10:51:30 +01:00
|
|
|
}
|
|
|
|
|
2018-07-12 15:14:23 +01:00
|
|
|
int syscall_trace_enter(struct pt_regs *regs)
|
2014-04-30 10:51:30 +01:00
|
|
|
{
|
2021-11-29 13:06:48 +00:00
|
|
|
unsigned long flags = read_thread_flags();
|
2020-05-15 18:22:53 -04:00
|
|
|
|
|
|
|
if (flags & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE)) {
|
2022-01-27 11:27:10 -06:00
|
|
|
report_syscall(regs, PTRACE_SYSCALL_ENTER);
|
2021-01-16 15:18:54 +00:00
|
|
|
if (flags & _TIF_SYSCALL_EMU)
|
2020-07-10 13:20:57 +01:00
|
|
|
return NO_SYSCALL;
|
2019-05-23 10:06:18 +01:00
|
|
|
}
|
2012-03-05 11:49:33 +00:00
|
|
|
|
2016-06-02 12:28:52 -07:00
|
|
|
/* Do the secure computing after ptrace; failures should be fast. */
|
2019-09-24 08:44:20 +02:00
|
|
|
if (secure_computing() == -1)
|
2020-07-10 13:20:57 +01:00
|
|
|
return NO_SYSCALL;
|
2016-06-02 12:28:52 -07:00
|
|
|
|
2014-04-30 10:54:36 +01:00
|
|
|
if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
|
|
|
|
trace_sys_enter(regs, regs->syscallno);
|
|
|
|
|
2014-09-23 16:25:34 -04:00
|
|
|
audit_syscall_entry(regs->syscallno, regs->orig_x0, regs->regs[1],
|
|
|
|
regs->regs[2], regs->regs[3]);
|
2014-07-04 08:28:31 +01:00
|
|
|
|
2012-03-05 11:49:33 +00:00
|
|
|
return regs->syscallno;
|
|
|
|
}
|
2014-04-30 10:51:30 +01:00
|
|
|
|
2018-07-12 15:14:23 +01:00
|
|
|
void syscall_trace_exit(struct pt_regs *regs)
|
2014-04-30 10:51:30 +01:00
|
|
|
{
|
2021-11-29 13:06:48 +00:00
|
|
|
unsigned long flags = read_thread_flags();
|
2020-07-02 21:16:20 +01:00
|
|
|
|
2014-07-04 08:28:31 +01:00
|
|
|
audit_syscall_exit(regs);
|
|
|
|
|
2020-07-02 21:16:20 +01:00
|
|
|
if (flags & _TIF_SYSCALL_TRACEPOINT)
|
arm64: fix compat syscall return truncation
Due to inconsistencies in the way we manipulate compat GPRs, we have a
few issues today:
* For audit and tracing, where error codes are handled as a (native)
long, negative error codes are expected to be sign-extended to the
native 64-bits, or they may fail to be matched correctly. Thus a
syscall which fails with an error may erroneously be identified as
failing.
* For ptrace, *all* compat return values should be sign-extended for
consistency with 32-bit arm, but we currently only do this for
negative return codes.
* As we may transiently set the upper 32 bits of some compat GPRs while
in the kernel, these can be sampled by perf, which is somewhat
confusing. This means that where a syscall returns a pointer above 2G,
this will be sign-extended, but will not be mistaken for an error as
error codes are constrained to the inclusive range [-4096, -1] where
no user pointer can exist.
To fix all of these, we must consistently use helpers to get/set the
compat GPRs, ensuring that we never write the upper 32 bits of the
return code, and always sign-extend when reading the return code. This
patch does so, with the following changes:
* We re-organise syscall_get_return_value() to always sign-extend for
compat tasks, and reimplement syscall_get_error() atop. We update
syscall_trace_exit() to use syscall_get_return_value().
* We consistently use syscall_set_return_value() to set the return
value, ensureing the upper 32 bits are never set unexpectedly.
* As the core audit code currently uses regs_return_value() rather than
syscall_get_return_value(), we special-case this for
compat_user_mode(regs) such that this will do the right thing. Going
forward, we should try to move the core audit code over to
syscall_get_return_value().
Cc: <stable@vger.kernel.org>
Reported-by: He Zhe <zhe.he@windriver.com>
Reported-by: weiyuchen <weiyuchen3@huawei.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/20210802104200.21390-1-mark.rutland@arm.com
Signed-off-by: Will Deacon <will@kernel.org>
2021-08-02 11:42:00 +01:00
|
|
|
trace_sys_exit(regs, syscall_get_return_value(current, regs));
|
2014-04-30 10:54:36 +01:00
|
|
|
|
2020-07-02 21:16:20 +01:00
|
|
|
if (flags & (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP))
|
2022-01-27 11:27:10 -06:00
|
|
|
report_syscall(regs, PTRACE_SYSCALL_EXIT);
|
2018-06-20 14:46:50 +01:00
|
|
|
|
|
|
|
rseq_syscall(regs);
|
2014-04-30 10:51:30 +01:00
|
|
|
}
|
2016-03-01 14:18:50 +00:00
|
|
|
|
|
|
|
/*
|
2019-02-15 16:34:27 +00:00
|
|
|
* SPSR_ELx bits which are always architecturally RES0 per ARM DDI 0487D.a.
|
|
|
|
* We permit userspace to set SSBS (AArch64 bit 12, AArch32 bit 23) which is
|
|
|
|
* not described in ARM DDI 0487D.a.
|
|
|
|
* We treat PAN and UAO as RES0 bits, as they are meaningless at EL0, and may
|
|
|
|
* be allocated an EL0 meaning in future.
|
2016-03-01 14:18:50 +00:00
|
|
|
* Userspace cannot use these until they have an architectural meaning.
|
2018-07-05 15:16:49 +01:00
|
|
|
* Note that this follows the SPSR_ELx format, not the AArch32 PSR format.
|
2016-03-01 14:18:50 +00:00
|
|
|
* We also reserve IL for the kernel; SS is handled dynamically.
|
|
|
|
*/
|
|
|
|
#define SPSR_EL1_AARCH64_RES0_BITS \
|
2019-09-06 10:55:29 +01:00
|
|
|
(GENMASK_ULL(63, 32) | GENMASK_ULL(27, 26) | GENMASK_ULL(23, 22) | \
|
2020-03-16 16:50:45 +00:00
|
|
|
GENMASK_ULL(20, 13) | GENMASK_ULL(5, 5))
|
2016-03-01 14:18:50 +00:00
|
|
|
#define SPSR_EL1_AARCH32_RES0_BITS \
|
2019-02-15 16:34:27 +00:00
|
|
|
(GENMASK_ULL(63, 32) | GENMASK_ULL(22, 22) | GENMASK_ULL(20, 20))
|
2016-03-01 14:18:50 +00:00
|
|
|
|
|
|
|
static int valid_compat_regs(struct user_pt_regs *regs)
|
|
|
|
{
|
|
|
|
regs->pstate &= ~SPSR_EL1_AARCH32_RES0_BITS;
|
|
|
|
|
|
|
|
if (!system_supports_mixed_endian_el0()) {
|
|
|
|
if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
|
2018-07-05 15:16:52 +01:00
|
|
|
regs->pstate |= PSR_AA32_E_BIT;
|
2016-03-01 14:18:50 +00:00
|
|
|
else
|
2018-07-05 15:16:52 +01:00
|
|
|
regs->pstate &= ~PSR_AA32_E_BIT;
|
2016-03-01 14:18:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (user_mode(regs) && (regs->pstate & PSR_MODE32_BIT) &&
|
2018-07-05 15:16:52 +01:00
|
|
|
(regs->pstate & PSR_AA32_A_BIT) == 0 &&
|
|
|
|
(regs->pstate & PSR_AA32_I_BIT) == 0 &&
|
|
|
|
(regs->pstate & PSR_AA32_F_BIT) == 0) {
|
2016-03-01 14:18:50 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Force PSR to a valid 32-bit EL0t, preserving the same bits as
|
|
|
|
* arch/arm.
|
|
|
|
*/
|
2018-07-05 15:16:52 +01:00
|
|
|
regs->pstate &= PSR_AA32_N_BIT | PSR_AA32_Z_BIT |
|
|
|
|
PSR_AA32_C_BIT | PSR_AA32_V_BIT |
|
|
|
|
PSR_AA32_Q_BIT | PSR_AA32_IT_MASK |
|
|
|
|
PSR_AA32_GE_MASK | PSR_AA32_E_BIT |
|
|
|
|
PSR_AA32_T_BIT;
|
2016-03-01 14:18:50 +00:00
|
|
|
regs->pstate |= PSR_MODE32_BIT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int valid_native_regs(struct user_pt_regs *regs)
|
|
|
|
{
|
|
|
|
regs->pstate &= ~SPSR_EL1_AARCH64_RES0_BITS;
|
|
|
|
|
|
|
|
if (user_mode(regs) && !(regs->pstate & PSR_MODE32_BIT) &&
|
|
|
|
(regs->pstate & PSR_D_BIT) == 0 &&
|
|
|
|
(regs->pstate & PSR_A_BIT) == 0 &&
|
|
|
|
(regs->pstate & PSR_I_BIT) == 0 &&
|
|
|
|
(regs->pstate & PSR_F_BIT) == 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Force PSR to a valid 64-bit EL0t */
|
|
|
|
regs->pstate &= PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Are the current registers suitable for user mode? (used to maintain
|
|
|
|
* security in signal handlers)
|
|
|
|
*/
|
|
|
|
int valid_user_regs(struct user_pt_regs *regs, struct task_struct *task)
|
|
|
|
{
|
arm64: ptrace: Override SPSR.SS when single-stepping is enabled
Luis reports that, when reverse debugging with GDB, single-step does not
function as expected on arm64:
| I've noticed, under very specific conditions, that a PTRACE_SINGLESTEP
| request by GDB won't execute the underlying instruction. As a consequence,
| the PC doesn't move, but we return a SIGTRAP just like we would for a
| regular successful PTRACE_SINGLESTEP request.
The underlying problem is that when the CPU register state is restored
as part of a reverse step, the SPSR.SS bit is cleared and so the hardware
single-step state can transition to the "active-pending" state, causing
an unexpected step exception to be taken immediately if a step operation
is attempted.
In hindsight, we probably shouldn't have exposed SPSR.SS in the pstate
accessible by the GPR regset, but it's a bit late for that now. Instead,
simply prevent userspace from configuring the bit to a value which is
inconsistent with the TIF_SINGLESTEP state for the task being traced.
Cc: <stable@vger.kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Keno Fischer <keno@juliacomputing.com>
Link: https://lore.kernel.org/r/1eed6d69-d53d-9657-1fc9-c089be07f98c@linaro.org
Reported-by: Luis Machado <luis.machado@linaro.org>
Tested-by: Luis Machado <luis.machado@linaro.org>
Signed-off-by: Will Deacon <will@kernel.org>
2020-02-13 12:06:26 +00:00
|
|
|
/* https://lore.kernel.org/lkml/20191118131525.GA4180@willie-the-truck */
|
|
|
|
user_regs_reset_single_step(regs, task);
|
2016-03-01 14:18:50 +00:00
|
|
|
|
|
|
|
if (is_compat_thread(task_thread_info(task)))
|
|
|
|
return valid_compat_regs(regs);
|
|
|
|
else
|
|
|
|
return valid_native_regs(regs);
|
|
|
|
}
|