cregit-Linux how code gets into the kernel

Release 4.14 arch/powerpc/lib/sstep.c

Directory: arch/powerpc/lib
/*
 * Single-step support.
 *
 * Copyright (C) 2004 Paul Mackerras <paulus@au.ibm.com>, IBM
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/ptrace.h>
#include <linux/prefetch.h>
#include <asm/sstep.h>
#include <asm/processor.h>
#include <linux/uaccess.h>
#include <asm/cpu_has_feature.h>
#include <asm/cputable.h>

extern char system_call_common[];

#ifdef CONFIG_PPC64
/* Bits in SRR1 that are copied from MSR */

#define MSR_MASK	0xffffffff87c0ffffUL
#else

#define MSR_MASK	0x87c0ffff
#endif

/* Bits in XER */

#define XER_SO		0x80000000U

#define XER_OV		0x40000000U

#define XER_CA		0x20000000U

#ifdef CONFIG_PPC_FPU
/*
 * Functions in ldstfp.S
 */
extern void get_fpr(int rn, double *p);
extern void put_fpr(int rn, const double *p);
extern void get_vr(int rn, __vector128 *p);
extern void put_vr(int rn, __vector128 *p);
extern void load_vsrn(int vsr, const void *p);
extern void store_vsrn(int vsr, void *p);
extern void conv_sp_to_dp(const float *sp, double *dp);
extern void conv_dp_to_sp(const double *dp, float *sp);
#endif

#ifdef __powerpc64__
/*
 * Functions in quad.S
 */
extern int do_lq(unsigned long ea, unsigned long *regs);
extern int do_stq(unsigned long ea, unsigned long val0, unsigned long val1);
extern int do_lqarx(unsigned long ea, unsigned long *regs);
extern int do_stqcx(unsigned long ea, unsigned long val0, unsigned long val1,
		    unsigned int *crp);
#endif

#ifdef __LITTLE_ENDIAN__

#define IS_LE	1

#define IS_BE	0
#else

#define IS_LE	0

#define IS_BE	1
#endif

/*
 * Emulate the truncation of 64 bit values in 32-bit mode.
 */

static nokprobe_inline unsigned long truncate_if_32bit(unsigned long msr, unsigned long val) { #ifdef __powerpc64__ if ((msr & MSR_64BIT) == 0) val &= 0xffffffffUL; #endif return val; }

Contributors

PersonTokensPropCommitsCommitProp
Michael Ellerman3797.37%150.00%
Naveen N. Rao12.63%150.00%
Total38100.00%2100.00%

/* * Determine whether a conditional branch instruction would branch. */
static nokprobe_inline int branch_taken(unsigned int instr, const struct pt_regs *regs, struct instruction_op *op) { unsigned int bo = (instr >> 21) & 0x1f; unsigned int bi; if ((bo & 4) == 0) { /* decrement counter */ op->type |= DECCTR; if (((bo >> 1) & 1) ^ (regs->ctr == 1)) return 0; } if ((bo & 0x10) == 0) { /* check bit from CR */ bi = (instr >> 16) & 0x1f; if (((regs->ccr >> (31 - bi)) & 1) != ((bo >> 3) & 1)) return 0; } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras13699.27%266.67%
Naveen N. Rao10.73%133.33%
Total137100.00%3100.00%


static nokprobe_inline long address_ok(struct pt_regs *regs, unsigned long ea, int nb) { if (!user_mode(regs)) return 1; if (__access_ok(ea, nb, USER_DS)) return 1; if (__access_ok(ea, 1, USER_DS)) /* Access overlaps the end of the user region */ regs->dar = USER_DS.seg; else regs->dar = ea; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras7398.65%375.00%
Naveen N. Rao11.35%125.00%
Total74100.00%4100.00%

/* * Calculate effective address for a D-form instruction */
static nokprobe_inline unsigned long dform_ea(unsigned int instr, const struct pt_regs *regs) { int ra; unsigned long ea; ra = (instr >> 16) & 0x1f; ea = (signed short) instr; /* sign-extend */ if (ra) ea += regs->gpr[ra]; return ea; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras5896.67%360.00%
Michael Ellerman11.67%120.00%
Naveen N. Rao11.67%120.00%
Total60100.00%5100.00%

#ifdef __powerpc64__ /* * Calculate effective address for a DS-form instruction */
static nokprobe_inline unsigned long dsform_ea(unsigned int instr, const struct pt_regs *regs) { int ra; unsigned long ea; ra = (instr >> 16) & 0x1f; ea = (signed short) (instr & ~3); /* sign-extend */ if (ra) ea += regs->gpr[ra]; return ea; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras6396.92%360.00%
Naveen N. Rao11.54%120.00%
Michael Ellerman11.54%120.00%
Total65100.00%5100.00%

/* * Calculate effective address for a DQ-form instruction */
static nokprobe_inline unsigned long dqform_ea(unsigned int instr, const struct pt_regs *regs) { int ra; unsigned long ea; ra = (instr >> 16) & 0x1f; ea = (signed short) (instr & ~0xf); /* sign-extend */ if (ra) ea += regs->gpr[ra]; return ea; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras65100.00%1100.00%
Total65100.00%1100.00%

#endif /* __powerpc64 */ /* * Calculate effective address for an X-form instruction */
static nokprobe_inline unsigned long xform_ea(unsigned int instr, const struct pt_regs *regs) { int ra, rb; unsigned long ea; ra = (instr >> 16) & 0x1f; rb = (instr >> 11) & 0x1f; ea = regs->gpr[rb]; if (ra) ea += regs->gpr[ra]; return ea; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras7097.22%360.00%
Naveen N. Rao11.39%120.00%
Michael Ellerman11.39%120.00%
Total72100.00%5100.00%

/* * Return the largest power of 2, not greater than sizeof(unsigned long), * such that x is a multiple of it. */
static nokprobe_inline unsigned long max_align(unsigned long x) { x |= sizeof(unsigned long); return x & -x; /* isolates rightmost bit */ }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras2696.30%150.00%
Naveen N. Rao13.70%150.00%
Total27100.00%2100.00%


static nokprobe_inline unsigned long byterev_2(unsigned long x) { return ((x >> 8) & 0xff) | ((x & 0xff) << 8); }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras3296.97%150.00%
Naveen N. Rao13.03%150.00%
Total33100.00%2100.00%


static nokprobe_inline unsigned long byterev_4(unsigned long x) { return ((x >> 24) & 0xff) | ((x >> 8) & 0xff00) | ((x & 0xff00) << 8) | ((x & 0xff) << 24); }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras5298.11%150.00%
Naveen N. Rao11.89%150.00%
Total53100.00%2100.00%

#ifdef __powerpc64__
static nokprobe_inline unsigned long byterev_8(unsigned long x) { return (byterev_4(x) << 32) | byterev_4(x >> 32); }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras2896.55%150.00%
Naveen N. Rao13.45%150.00%
Total29100.00%2100.00%

#endif
static nokprobe_inline void do_byte_reverse(void *ptr, int nb) { switch (nb) { case 2: *(u16 *)ptr = byterev_2(*(u16 *)ptr); break; case 4: *(u32 *)ptr = byterev_4(*(u32 *)ptr); break; #ifdef __powerpc64__ case 8: *(unsigned long *)ptr = byterev_8(*(unsigned long *)ptr); break; case 16: { unsigned long *up = (unsigned long *)ptr; unsigned long tmp; tmp = byterev_8(up[0]); up[0] = byterev_8(up[1]); up[1] = tmp; break; } #endif default: WARN_ON_ONCE(1); } }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras148100.00%1100.00%
Total148100.00%1100.00%


static nokprobe_inline int read_mem_aligned(unsigned long *dest, unsigned long ea, int nb, struct pt_regs *regs) { int err = 0; unsigned long x = 0; switch (nb) { case 1: err = __get_user(x, (unsigned char __user *) ea); break; case 2: err = __get_user(x, (unsigned short __user *) ea); break; case 4: err = __get_user(x, (unsigned int __user *) ea); break; #ifdef __powerpc64__ case 8: err = __get_user(x, (unsigned long __user *) ea); break; #endif } if (!err) *dest = x; else regs->dar = ea; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras14199.30%266.67%
Naveen N. Rao10.70%133.33%
Total142100.00%3100.00%

/* * Copy from userspace to a buffer, using the largest possible * aligned accesses, up to sizeof(long). */
static int nokprobe_inline copy_mem_in(u8 *dest, unsigned long ea, int nb, struct pt_regs *regs) { int err = 0; int c; for (; nb > 0; nb -= c) { c = max_align(ea); if (c > nb) c = max_align(nb); switch (c) { case 1: err = __get_user(*dest, (unsigned char __user *) ea); break; case 2: err = __get_user(*(u16 *)dest, (unsigned short __user *) ea); break; case 4: err = __get_user(*(u32 *)dest, (unsigned int __user *) ea); break; #ifdef __powerpc64__ case 8: err = __get_user(*(unsigned long *)dest, (unsigned long __user *) ea); break; #endif } if (err) { regs->dar = ea; return err; } dest += c; ea += c; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras16283.51%375.00%
Tom Musta3216.49%125.00%
Total194100.00%4100.00%


static nokprobe_inline int read_mem_unaligned(unsigned long *dest, unsigned long ea, int nb, struct pt_regs *regs) { union { unsigned long ul; u8 b[sizeof(unsigned long)]; } u; int i; int err; u.ul = 0; i = IS_BE ? sizeof(unsigned long) - nb : 0; err = copy_mem_in(&u.b[i], ea, nb, regs); if (!err) *dest = u.ul; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras103100.00%3100.00%
Total103100.00%3100.00%

/* * Read memory at address ea for nb bytes, return 0 for success * or -EFAULT if an error occurred. N.B. nb must be 1, 2, 4 or 8. * If nb < sizeof(long), the result is right-justified on BE systems. */
static int read_mem(unsigned long *dest, unsigned long ea, int nb, struct pt_regs *regs) { if (!address_ok(regs, ea, nb)) return -EFAULT; if ((ea & (nb - 1)) == 0) return read_mem_aligned(dest, ea, nb, regs); return read_mem_unaligned(dest, ea, nb, regs); }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras77100.00%2100.00%
Total77100.00%2100.00%

NOKPROBE_SYMBOL(read_mem);
static nokprobe_inline int write_mem_aligned(unsigned long val, unsigned long ea, int nb, struct pt_regs *regs) { int err = 0; switch (nb) { case 1: err = __put_user(val, (unsigned char __user *) ea); break; case 2: err = __put_user(val, (unsigned short __user *) ea); break; case 4: err = __put_user(val, (unsigned int __user *) ea); break; #ifdef __powerpc64__ case 8: err = __put_user(val, (unsigned long __user *) ea); break; #endif } if (err) regs->dar = ea; return err; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras12799.22%266.67%
Naveen N. Rao10.78%133.33%
Total128100.00%3100.00%

/* * Copy from a buffer to userspace, using the largest possible * aligned accesses, up to sizeof(long). */
static int nokprobe_inline copy_mem_out(u8 *dest, unsigned long ea, int nb, struct pt_regs *regs) { int err = 0; int c; for (; nb > 0; nb -= c) { c = max_align(ea); if (c > nb) c = max_align(nb); switch (c) { case 1: err = __put_user(*dest, (unsigned char __user *) ea); break; case 2: err = __put_user(*(u16 *)dest, (unsigned short __user *) ea); break; case 4: err = __put_user(*(u32 *)dest, (unsigned int __user *) ea); break; #ifdef __powerpc64__ case 8: err = __put_user(*(unsigned long *)dest, (unsigned long __user *) ea); break; #endif } if (err) { regs->dar = ea; return err; } dest += c; ea += c; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras19298.97%375.00%
Tom Musta21.03%125.00%
Total194100.00%4100.00%


static nokprobe_inline int write_mem_unaligned(unsigned long val, unsigned long ea, int nb, struct pt_regs *regs) { union { unsigned long ul; u8 b[sizeof(unsigned long)]; } u; int i; u.ul = val; i = IS_BE ? sizeof(unsigned long) - nb : 0; return copy_mem_out(&u.b[i], ea, nb, regs); }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras83100.00%2100.00%
Total83100.00%2100.00%

/* * Write memory at address ea for nb bytes, return 0 for success * or -EFAULT if an error occurred. N.B. nb must be 1, 2, 4 or 8. */
static int write_mem(unsigned long val, unsigned long ea, int nb, struct pt_regs *regs) { if (!address_ok(regs, ea, nb)) return -EFAULT; if ((ea & (nb - 1)) == 0) return write_mem_aligned(val, ea, nb, regs); return write_mem_unaligned(val, ea, nb, regs); }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras76100.00%2100.00%
Total76100.00%2100.00%

NOKPROBE_SYMBOL(write_mem); #ifdef CONFIG_PPC_FPU /* * These access either the real FP register or the image in the * thread_struct, depending on regs->msr & MSR_FP. */
static int do_fp_load(struct instruction_op *op, unsigned long ea, struct pt_regs *regs, bool cross_endian) { int err, rn, nb; union { int i; unsigned int u; float f; double d[2]; unsigned long l[2]; u8 b[2 * sizeof(double)]; } u; nb = GETSIZE(op->type); if (!address_ok(regs, ea, nb)) return -EFAULT; rn = op->reg; err = copy_mem_in(u.b, ea, nb, regs); if (err) return err; if (unlikely(cross_endian)) { do_byte_reverse(u.b, min(nb, 8)); if (nb == 16) do_byte_reverse(&u.b[8], 8); } preempt_disable(); if (nb == 4) { if (op->type & FPCONV) conv_sp_to_dp(&u.f, &u.d[0]); else if (op->type & SIGNEXT) u.l[0] = u.i; else u.l[0] = u.u; } if (regs->msr & MSR_FP) put_fpr(rn, &u.d[0]); else current->thread.TS_FPR(rn) = u.l[0]; if (nb == 16) { /* lfdp */ rn |= 1; if (regs->msr & MSR_FP) put_fpr(rn, &u.d[1]); else current->thread.TS_FPR(rn) = u.l[1]; } preempt_enable(); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras326100.00%7100.00%
Total326100.00%7100.00%

NOKPROBE_SYMBOL(do_fp_load);
static int do_fp_store(struct instruction_op *op, unsigned long ea, struct pt_regs *regs, bool cross_endian) { int rn, nb; union { unsigned int u; float f; double d[2]; unsigned long l[2]; u8 b[2 * sizeof(double)]; } u; nb = GETSIZE(op->type); if (!address_ok(regs, ea, nb)) return -EFAULT; rn = op->reg; preempt_disable(); if (regs->msr & MSR_FP) get_fpr(rn, &u.d[0]); else u.l[0] = current->thread.TS_FPR(rn); if (nb == 4) { if (op->type & FPCONV) conv_dp_to_sp(&u.d[0], &u.f); else u.u = u.l[0]; } if (nb == 16) { rn |= 1; if (regs->msr & MSR_FP) get_fpr(rn, &u.d[1]); else u.l[1] = current->thread.TS_FPR(rn); } preempt_enable(); if (unlikely(cross_endian)) { do_byte_reverse(u.b, min(nb, 8)); if (nb == 16) do_byte_reverse(&u.b[8], 8); } return copy_mem_out(u.b, ea, nb, regs); }

Contributors

PersonTokensPropCommitsCommitProp
Paul Mackerras289100.00%7100.00%
Total289100.00%7100.00%

NOKPROBE_SYMBOL(do_fp_store); #endif #ifdef CONFIG_ALTIVEC /* For Altivec/VMX, no need to worry about alignment */
static nokprobe_inline int do_vec_load(int rn, unsigned long ea, int size, struct pt_regs *regs, bool cross_endian) { int err; union { __vector128 v; u8 b[sizeof(__vector128)]; } u = {}; if (!address_ok(regs, ea & ~0xfUL, 16)) return -EFAULT; /* align to multiple of size */ ea &= ~(size - 1); err = copy_mem_in(&u.b[ea & 0xf], ea, size, regs); if (err) return err; if (unlikely(cross_endian)) do_byte_reverse(&u.b[ea & 0xf], size); preempt_disable(); if (regs->msr & MSR_VEC) put_vr(rn, &u.v); else current->thread.vr_state.vr[rn] = u.v; preempt_enable