Skip to content

Commit 0a8ea52

Browse files
David A. Longctmarinas
authored andcommitted
arm64: Add HAVE_REGS_AND_STACK_ACCESS_API feature
Add HAVE_REGS_AND_STACK_ACCESS_API feature for arm64, including supporting functions and defines. Signed-off-by: David A. Long <[email protected]> Acked-by: Masami Hiramatsu <[email protected]> [[email protected]: Remove unused functions] Signed-off-by: Catalin Marinas <[email protected]>
1 parent 33688ab commit 0a8ea52

File tree

4 files changed

+152
-1
lines changed

4 files changed

+152
-1
lines changed

arch/arm/include/asm/ptrace.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ extern unsigned long profile_pc(struct pt_regs *regs);
121121
#define MAX_REG_OFFSET (offsetof(struct pt_regs, ARM_ORIG_r0))
122122

123123
extern int regs_query_register_offset(const char *name);
124-
extern const char *regs_query_register_name(unsigned int offset);
125124
extern bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr);
126125
extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
127126
unsigned int n);

arch/arm64/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ config ARM64
8585
select HAVE_PERF_EVENTS
8686
select HAVE_PERF_REGS
8787
select HAVE_PERF_USER_STACK_DUMP
88+
select HAVE_REGS_AND_STACK_ACCESS_API
8889
select HAVE_RCU_TABLE_FREE
8990
select HAVE_SYSCALL_TRACEPOINTS
9091
select IOMMU_DMA if IOMMU_SUPPORT

arch/arm64/include/asm/ptrace.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
#define COMPAT_PT_DATA_ADDR 0x10004
7575
#define COMPAT_PT_TEXT_END_ADDR 0x10008
7676
#ifndef __ASSEMBLY__
77+
#include <linux/bug.h>
7778

7879
/* sizeof(struct user) for AArch32 */
7980
#define COMPAT_USER_SZ 296
@@ -119,6 +120,8 @@ struct pt_regs {
119120
u64 syscallno;
120121
};
121122

123+
#define MAX_REG_OFFSET offsetof(struct pt_regs, pstate)
124+
122125
#define arch_has_single_step() (1)
123126

124127
#ifdef CONFIG_COMPAT
@@ -147,6 +150,53 @@ struct pt_regs {
147150
#define user_stack_pointer(regs) \
148151
(!compat_user_mode(regs) ? (regs)->sp : (regs)->compat_sp)
149152

153+
extern int regs_query_register_offset(const char *name);
154+
extern const char *regs_query_register_name(unsigned int offset);
155+
extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
156+
unsigned int n);
157+
158+
/**
159+
* regs_get_register() - get register value from its offset
160+
* @regs: pt_regs from which register value is gotten
161+
* @offset: offset of the register.
162+
*
163+
* regs_get_register returns the value of a register whose offset from @regs.
164+
* The @offset is the offset of the register in struct pt_regs.
165+
* If @offset is bigger than MAX_REG_OFFSET, this returns 0.
166+
*/
167+
static inline u64 regs_get_register(struct pt_regs *regs, unsigned int offset)
168+
{
169+
u64 val = 0;
170+
171+
WARN_ON(offset & 7);
172+
173+
offset >>= 3;
174+
switch (offset) {
175+
case 0 ... 30:
176+
val = regs->regs[offset];
177+
break;
178+
case offsetof(struct pt_regs, sp) >> 3:
179+
val = regs->sp;
180+
break;
181+
case offsetof(struct pt_regs, pc) >> 3:
182+
val = regs->pc;
183+
break;
184+
case offsetof(struct pt_regs, pstate) >> 3:
185+
val = regs->pstate;
186+
break;
187+
default:
188+
val = 0;
189+
}
190+
191+
return val;
192+
}
193+
194+
/* Valid only for Kernel mode traps. */
195+
static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
196+
{
197+
return regs->sp;
198+
}
199+
150200
static inline unsigned long regs_return_value(struct pt_regs *regs)
151201
{
152202
return regs->regs[0];

arch/arm64/kernel/ptrace.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,107 @@
4848
#define CREATE_TRACE_POINTS
4949
#include <trace/events/syscalls.h>
5050

51+
struct pt_regs_offset {
52+
const char *name;
53+
int offset;
54+
};
55+
56+
#define REG_OFFSET_NAME(r) {.name = #r, .offset = offsetof(struct pt_regs, r)}
57+
#define REG_OFFSET_END {.name = NULL, .offset = 0}
58+
#define GPR_OFFSET_NAME(r) \
59+
{.name = "x" #r, .offset = offsetof(struct pt_regs, regs[r])}
60+
61+
static const struct pt_regs_offset regoffset_table[] = {
62+
GPR_OFFSET_NAME(0),
63+
GPR_OFFSET_NAME(1),
64+
GPR_OFFSET_NAME(2),
65+
GPR_OFFSET_NAME(3),
66+
GPR_OFFSET_NAME(4),
67+
GPR_OFFSET_NAME(5),
68+
GPR_OFFSET_NAME(6),
69+
GPR_OFFSET_NAME(7),
70+
GPR_OFFSET_NAME(8),
71+
GPR_OFFSET_NAME(9),
72+
GPR_OFFSET_NAME(10),
73+
GPR_OFFSET_NAME(11),
74+
GPR_OFFSET_NAME(12),
75+
GPR_OFFSET_NAME(13),
76+
GPR_OFFSET_NAME(14),
77+
GPR_OFFSET_NAME(15),
78+
GPR_OFFSET_NAME(16),
79+
GPR_OFFSET_NAME(17),
80+
GPR_OFFSET_NAME(18),
81+
GPR_OFFSET_NAME(19),
82+
GPR_OFFSET_NAME(20),
83+
GPR_OFFSET_NAME(21),
84+
GPR_OFFSET_NAME(22),
85+
GPR_OFFSET_NAME(23),
86+
GPR_OFFSET_NAME(24),
87+
GPR_OFFSET_NAME(25),
88+
GPR_OFFSET_NAME(26),
89+
GPR_OFFSET_NAME(27),
90+
GPR_OFFSET_NAME(28),
91+
GPR_OFFSET_NAME(29),
92+
GPR_OFFSET_NAME(30),
93+
{.name = "lr", .offset = offsetof(struct pt_regs, regs[30])},
94+
REG_OFFSET_NAME(sp),
95+
REG_OFFSET_NAME(pc),
96+
REG_OFFSET_NAME(pstate),
97+
REG_OFFSET_END,
98+
};
99+
100+
/**
101+
* regs_query_register_offset() - query register offset from its name
102+
* @name: the name of a register
103+
*
104+
* regs_query_register_offset() returns the offset of a register in struct
105+
* pt_regs from its name. If the name is invalid, this returns -EINVAL;
106+
*/
107+
int regs_query_register_offset(const char *name)
108+
{
109+
const struct pt_regs_offset *roff;
110+
111+
for (roff = regoffset_table; roff->name != NULL; roff++)
112+
if (!strcmp(roff->name, name))
113+
return roff->offset;
114+
return -EINVAL;
115+
}
116+
117+
/**
118+
* regs_within_kernel_stack() - check the address in the stack
119+
* @regs: pt_regs which contains kernel stack pointer.
120+
* @addr: address which is checked.
121+
*
122+
* regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
123+
* If @addr is within the kernel stack, it returns true. If not, returns false.
124+
*/
125+
static bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
126+
{
127+
return ((addr & ~(THREAD_SIZE - 1)) ==
128+
(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))) ||
129+
on_irq_stack(addr, raw_smp_processor_id());
130+
}
131+
132+
/**
133+
* regs_get_kernel_stack_nth() - get Nth entry of the stack
134+
* @regs: pt_regs which contains kernel stack pointer.
135+
* @n: stack entry number.
136+
*
137+
* regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
138+
* is specified by @regs. If the @n th entry is NOT in the kernel stack,
139+
* this returns 0.
140+
*/
141+
unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
142+
{
143+
unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
144+
145+
addr += n;
146+
if (regs_within_kernel_stack(regs, (unsigned long)addr))
147+
return *addr;
148+
else
149+
return 0;
150+
}
151+
51152
/*
52153
* TODO: does not yet catch signals sent when the child dies.
53154
* in exit.c or in signal.c.

0 commit comments

Comments
 (0)