| .. | .. |
|---|
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Based on arch/arm/include/asm/ptrace.h |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 1996-2003 Russell King |
|---|
| 5 | 6 | * Copyright (C) 2012 ARM Ltd. |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 9 | | - * published by the Free Software Foundation. |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | | - * GNU General Public License for more details. |
|---|
| 15 | | - * |
|---|
| 16 | | - * You should have received a copy of the GNU General Public License |
|---|
| 17 | | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 18 | 7 | */ |
|---|
| 19 | 8 | #ifndef __ASM_PTRACE_H |
|---|
| 20 | 9 | #define __ASM_PTRACE_H |
|---|
| 10 | + |
|---|
| 11 | +#include <asm/cpufeature.h> |
|---|
| 21 | 12 | |
|---|
| 22 | 13 | #include <uapi/asm/ptrace.h> |
|---|
| 23 | 14 | |
|---|
| 24 | 15 | /* Current Exception Level values, as contained in CurrentEL */ |
|---|
| 25 | 16 | #define CurrentEL_EL1 (1 << 2) |
|---|
| 26 | 17 | #define CurrentEL_EL2 (2 << 2) |
|---|
| 18 | + |
|---|
| 19 | +#define INIT_PSTATE_EL1 \ |
|---|
| 20 | + (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL1h) |
|---|
| 21 | +#define INIT_PSTATE_EL2 \ |
|---|
| 22 | + (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT | PSR_MODE_EL2h) |
|---|
| 23 | + |
|---|
| 24 | +/* |
|---|
| 25 | + * PMR values used to mask/unmask interrupts. |
|---|
| 26 | + * |
|---|
| 27 | + * GIC priority masking works as follows: if an IRQ's priority is a higher value |
|---|
| 28 | + * than the value held in PMR, that IRQ is masked. Lowering the value of PMR |
|---|
| 29 | + * means masking more IRQs (or at least that the same IRQs remain masked). |
|---|
| 30 | + * |
|---|
| 31 | + * To mask interrupts, we clear the most significant bit of PMR. |
|---|
| 32 | + * |
|---|
| 33 | + * Some code sections either automatically switch back to PSR.I or explicitly |
|---|
| 34 | + * require to not use priority masking. If bit GIC_PRIO_PSR_I_SET is included |
|---|
| 35 | + * in the priority mask, it indicates that PSR.I should be set and |
|---|
| 36 | + * interrupt disabling temporarily does not rely on IRQ priorities. |
|---|
| 37 | + */ |
|---|
| 38 | +#define GIC_PRIO_IRQON 0xe0 |
|---|
| 39 | +#define __GIC_PRIO_IRQOFF (GIC_PRIO_IRQON & ~0x80) |
|---|
| 40 | +#define __GIC_PRIO_IRQOFF_NS 0xa0 |
|---|
| 41 | +#define GIC_PRIO_PSR_I_SET (1 << 4) |
|---|
| 42 | + |
|---|
| 43 | +#define GIC_PRIO_IRQOFF \ |
|---|
| 44 | + ({ \ |
|---|
| 45 | + extern struct static_key_false gic_nonsecure_priorities;\ |
|---|
| 46 | + u8 __prio = __GIC_PRIO_IRQOFF; \ |
|---|
| 47 | + \ |
|---|
| 48 | + if (static_branch_unlikely(&gic_nonsecure_priorities)) \ |
|---|
| 49 | + __prio = __GIC_PRIO_IRQOFF_NS; \ |
|---|
| 50 | + \ |
|---|
| 51 | + __prio; \ |
|---|
| 52 | + }) |
|---|
| 53 | + |
|---|
| 54 | +/* Additional SPSR bits not exposed in the UABI */ |
|---|
| 55 | +#define PSR_MODE_THREAD_BIT (1 << 0) |
|---|
| 56 | +#define PSR_IL_BIT (1 << 20) |
|---|
| 27 | 57 | |
|---|
| 28 | 58 | /* AArch32-specific ptrace requests */ |
|---|
| 29 | 59 | #define COMPAT_PTRACE_GETREGS 12 |
|---|
| .. | .. |
|---|
| 165 | 195 | #endif |
|---|
| 166 | 196 | |
|---|
| 167 | 197 | u64 orig_addr_limit; |
|---|
| 168 | | - u64 unused; // maintain 16 byte alignment |
|---|
| 198 | + /* Only valid when ARM64_HAS_IRQ_PRIO_MASKING is enabled. */ |
|---|
| 199 | + u64 pmr_save; |
|---|
| 169 | 200 | u64 stackframe[2]; |
|---|
| 201 | + |
|---|
| 202 | + /* Only valid for some EL1 exceptions. */ |
|---|
| 203 | + u64 lockdep_hardirqs; |
|---|
| 204 | + u64 exit_rcu; |
|---|
| 170 | 205 | }; |
|---|
| 171 | 206 | |
|---|
| 172 | 207 | static inline bool in_syscall(struct pt_regs const *regs) |
|---|
| .. | .. |
|---|
| 200 | 235 | #define processor_mode(regs) \ |
|---|
| 201 | 236 | ((regs)->pstate & PSR_MODE_MASK) |
|---|
| 202 | 237 | |
|---|
| 203 | | -#define interrupts_enabled(regs) \ |
|---|
| 204 | | - (!((regs)->pstate & PSR_I_BIT)) |
|---|
| 238 | +#define irqs_priority_unmasked(regs) \ |
|---|
| 239 | + (system_uses_irq_prio_masking() ? \ |
|---|
| 240 | + (regs)->pmr_save == GIC_PRIO_IRQON : \ |
|---|
| 241 | + true) |
|---|
| 242 | + |
|---|
| 243 | +#define interrupts_enabled(regs) \ |
|---|
| 244 | + (!((regs)->pstate & PSR_I_BIT) && irqs_priority_unmasked(regs)) |
|---|
| 205 | 245 | |
|---|
| 206 | 246 | #define fast_interrupts_enabled(regs) \ |
|---|
| 207 | 247 | (!((regs)->pstate & PSR_F_BIT)) |
|---|
| 208 | 248 | |
|---|
| 209 | | -#define GET_USP(regs) \ |
|---|
| 210 | | - (!compat_user_mode(regs) ? (regs)->sp : (regs)->compat_sp) |
|---|
| 211 | | - |
|---|
| 212 | | -#define SET_USP(ptregs, value) \ |
|---|
| 213 | | - (!compat_user_mode(regs) ? ((regs)->sp = value) : ((regs)->compat_sp = value)) |
|---|
| 249 | +static inline unsigned long user_stack_pointer(struct pt_regs *regs) |
|---|
| 250 | +{ |
|---|
| 251 | + if (compat_user_mode(regs)) |
|---|
| 252 | + return regs->compat_sp; |
|---|
| 253 | + return regs->sp; |
|---|
| 254 | +} |
|---|
| 214 | 255 | |
|---|
| 215 | 256 | extern int regs_query_register_offset(const char *name); |
|---|
| 216 | 257 | extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, |
|---|
| .. | .. |
|---|
| 280 | 321 | |
|---|
| 281 | 322 | static inline unsigned long regs_return_value(struct pt_regs *regs) |
|---|
| 282 | 323 | { |
|---|
| 283 | | - return regs->regs[0]; |
|---|
| 324 | + unsigned long val = regs->regs[0]; |
|---|
| 325 | + |
|---|
| 326 | + /* |
|---|
| 327 | + * Audit currently uses regs_return_value() instead of |
|---|
| 328 | + * syscall_get_return_value(). Apply the same sign-extension here until |
|---|
| 329 | + * audit is updated to use syscall_get_return_value(). |
|---|
| 330 | + */ |
|---|
| 331 | + if (compat_user_mode(regs)) |
|---|
| 332 | + val = sign_extend64(val, 31); |
|---|
| 333 | + |
|---|
| 334 | + return val; |
|---|
| 335 | +} |
|---|
| 336 | + |
|---|
| 337 | +static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc) |
|---|
| 338 | +{ |
|---|
| 339 | + regs->regs[0] = rc; |
|---|
| 340 | +} |
|---|
| 341 | + |
|---|
| 342 | +/** |
|---|
| 343 | + * regs_get_kernel_argument() - get Nth function argument in kernel |
|---|
| 344 | + * @regs: pt_regs of that context |
|---|
| 345 | + * @n: function argument number (start from 0) |
|---|
| 346 | + * |
|---|
| 347 | + * regs_get_argument() returns @n th argument of the function call. |
|---|
| 348 | + * |
|---|
| 349 | + * Note that this chooses the most likely register mapping. In very rare |
|---|
| 350 | + * cases this may not return correct data, for example, if one of the |
|---|
| 351 | + * function parameters is 16 bytes or bigger. In such cases, we cannot |
|---|
| 352 | + * get access the parameter correctly and the register assignment of |
|---|
| 353 | + * subsequent parameters will be shifted. |
|---|
| 354 | + */ |
|---|
| 355 | +static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs, |
|---|
| 356 | + unsigned int n) |
|---|
| 357 | +{ |
|---|
| 358 | +#define NR_REG_ARGUMENTS 8 |
|---|
| 359 | + if (n < NR_REG_ARGUMENTS) |
|---|
| 360 | + return pt_regs_read_reg(regs, n); |
|---|
| 361 | + return 0; |
|---|
| 284 | 362 | } |
|---|
| 285 | 363 | |
|---|
| 286 | 364 | /* We must avoid circular header include via sched.h */ |
|---|
| 287 | 365 | struct task_struct; |
|---|
| 288 | 366 | int valid_user_regs(struct user_pt_regs *regs, struct task_struct *task); |
|---|
| 289 | 367 | |
|---|
| 290 | | -#define GET_IP(regs) ((unsigned long)(regs)->pc) |
|---|
| 291 | | -#define SET_IP(regs, value) ((regs)->pc = ((u64) (value))) |
|---|
| 368 | +static inline unsigned long instruction_pointer(struct pt_regs *regs) |
|---|
| 369 | +{ |
|---|
| 370 | + return regs->pc; |
|---|
| 371 | +} |
|---|
| 372 | +static inline void instruction_pointer_set(struct pt_regs *regs, |
|---|
| 373 | + unsigned long val) |
|---|
| 374 | +{ |
|---|
| 375 | + regs->pc = val; |
|---|
| 376 | +} |
|---|
| 292 | 377 | |
|---|
| 293 | | -#define GET_FP(ptregs) ((unsigned long)(ptregs)->regs[29]) |
|---|
| 294 | | -#define SET_FP(ptregs, value) ((ptregs)->regs[29] = ((u64) (value))) |
|---|
| 295 | | - |
|---|
| 296 | | -#include <asm-generic/ptrace.h> |
|---|
| 378 | +static inline unsigned long frame_pointer(struct pt_regs *regs) |
|---|
| 379 | +{ |
|---|
| 380 | + return regs->regs[29]; |
|---|
| 381 | +} |
|---|
| 297 | 382 | |
|---|
| 298 | 383 | #define procedure_link_pointer(regs) ((regs)->regs[30]) |
|---|
| 299 | 384 | |
|---|
| .. | .. |
|---|
| 303 | 388 | procedure_link_pointer(regs) = val; |
|---|
| 304 | 389 | } |
|---|
| 305 | 390 | |
|---|
| 306 | | -#undef profile_pc |
|---|
| 307 | 391 | extern unsigned long profile_pc(struct pt_regs *regs); |
|---|
| 308 | 392 | |
|---|
| 309 | 393 | #endif /* __ASSEMBLY__ */ |
|---|