mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 22:51:35 +00:00
Merge branch 'devel-stable' into for-next
This commit is contained in:
commit
df9ab9771c
@ -10,12 +10,13 @@ Optional properties:
|
||||
Each button (key) is represented as a sub-node of "gpio-keys":
|
||||
Subnode properties:
|
||||
|
||||
- gpios: OF device-tree gpio specification.
|
||||
- interrupts: the interrupt line for that input.
|
||||
- label: Descriptive name of the key.
|
||||
- linux,code: Keycode to emit.
|
||||
|
||||
Required mutual exclusive subnode-properties:
|
||||
- gpios: OF device-tree gpio specification.
|
||||
- interrupts: the interrupt line for that input
|
||||
Note that either "interrupts" or "gpios" properties can be omitted, but not
|
||||
both at the same time. Specifying both properties is allowed.
|
||||
|
||||
Optional subnode-properties:
|
||||
- linux,input-type: Specify event type this button/key generates.
|
||||
@ -23,6 +24,9 @@ Optional subnode-properties:
|
||||
- debounce-interval: Debouncing interval time in milliseconds.
|
||||
If not specified defaults to 5.
|
||||
- gpio-key,wakeup: Boolean, button can wake-up the system.
|
||||
- linux,can-disable: Boolean, indicates that button is connected
|
||||
to dedicated (not shared) interrupt which can be disabled to
|
||||
suppress events from the button.
|
||||
|
||||
Example nodes:
|
||||
|
||||
|
@ -8,6 +8,8 @@ Optional properties:
|
||||
- debounce-interval : Debouncing interval time in milliseconds
|
||||
- st,scan-count : Scanning cycles elapsed before key data is updated
|
||||
- st,no-autorepeat : If specified device will not autorepeat
|
||||
- keypad,num-rows : See ./matrix-keymap.txt
|
||||
- keypad,num-columns : See ./matrix-keymap.txt
|
||||
|
||||
Example:
|
||||
|
||||
|
2
Makefile
2
Makefile
@ -1,7 +1,7 @@
|
||||
VERSION = 3
|
||||
PATCHLEVEL = 19
|
||||
SUBLEVEL = 0
|
||||
EXTRAVERSION = -rc1
|
||||
EXTRAVERSION = -rc3
|
||||
NAME = Diseased Newt
|
||||
|
||||
# *DOCUMENTATION*
|
||||
|
@ -61,6 +61,7 @@ config ARM
|
||||
select HAVE_MEMBLOCK
|
||||
select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND
|
||||
select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
|
||||
select HAVE_OPTPROBES if !THUMB2_KERNEL
|
||||
select HAVE_PERF_EVENTS
|
||||
select HAVE_PERF_REGS
|
||||
select HAVE_PERF_USER_STACK_DUMP
|
||||
|
@ -266,6 +266,7 @@ core-$(CONFIG_KVM_ARM_HOST) += arch/arm/kvm/
|
||||
|
||||
# If we have a machine-specific directory, then include it in the build.
|
||||
core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
|
||||
core-y += arch/arm/probes/
|
||||
core-y += arch/arm/net/
|
||||
core-y += arch/arm/crypto/
|
||||
core-y += arch/arm/firmware/
|
||||
|
@ -203,27 +203,3 @@
|
||||
compatible = "linux,spdif-dir";
|
||||
};
|
||||
};
|
||||
|
||||
&pinctrl {
|
||||
/*
|
||||
* These pins might be muxed as I2S by
|
||||
* the bootloader, but it conflicts
|
||||
* with the real I2S pins that are
|
||||
* muxed using i2s_pins. We must mux
|
||||
* those pins to a function other than
|
||||
* I2S.
|
||||
*/
|
||||
pinctrl-0 = <&hog_pins1 &hog_pins2>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
hog_pins1: hog-pins1 {
|
||||
marvell,pins = "mpp6", "mpp8", "mpp10",
|
||||
"mpp12", "mpp13";
|
||||
marvell,function = "gpio";
|
||||
};
|
||||
|
||||
hog_pins2: hog-pins2 {
|
||||
marvell,pins = "mpp5", "mpp7", "mpp9";
|
||||
marvell,function = "gpo";
|
||||
};
|
||||
};
|
||||
|
@ -338,6 +338,7 @@ CONFIG_USB=y
|
||||
CONFIG_USB_XHCI_HCD=y
|
||||
CONFIG_USB_XHCI_MVEBU=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_EHCI_EXYNOS=y
|
||||
CONFIG_USB_EHCI_TEGRA=y
|
||||
CONFIG_USB_EHCI_HCD_STI=y
|
||||
CONFIG_USB_EHCI_HCD_PLATFORM=y
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#define __ARCH_WANT_KPROBES_INSN_SLOT
|
||||
#define MAX_INSN_SIZE 2
|
||||
#define MAX_STACK_SIZE 64 /* 32 would probably be OK */
|
||||
|
||||
#define flush_insn_slot(p) do { } while (0)
|
||||
#define kretprobe_blacklist_size 0
|
||||
@ -51,5 +50,37 @@ int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr);
|
||||
int kprobe_exceptions_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data);
|
||||
|
||||
/* optinsn template addresses */
|
||||
extern __visible kprobe_opcode_t optprobe_template_entry;
|
||||
extern __visible kprobe_opcode_t optprobe_template_val;
|
||||
extern __visible kprobe_opcode_t optprobe_template_call;
|
||||
extern __visible kprobe_opcode_t optprobe_template_end;
|
||||
extern __visible kprobe_opcode_t optprobe_template_sub_sp;
|
||||
extern __visible kprobe_opcode_t optprobe_template_add_sp;
|
||||
extern __visible kprobe_opcode_t optprobe_template_restore_begin;
|
||||
extern __visible kprobe_opcode_t optprobe_template_restore_orig_insn;
|
||||
extern __visible kprobe_opcode_t optprobe_template_restore_end;
|
||||
|
||||
#define MAX_OPTIMIZED_LENGTH 4
|
||||
#define MAX_OPTINSN_SIZE \
|
||||
((unsigned long)&optprobe_template_end - \
|
||||
(unsigned long)&optprobe_template_entry)
|
||||
#define RELATIVEJUMP_SIZE 4
|
||||
|
||||
struct arch_optimized_insn {
|
||||
/*
|
||||
* copy of the original instructions.
|
||||
* Different from x86, ARM kprobe_opcode_t is u32.
|
||||
*/
|
||||
#define MAX_COPIED_INSN DIV_ROUND_UP(RELATIVEJUMP_SIZE, sizeof(kprobe_opcode_t))
|
||||
kprobe_opcode_t copied_insn[MAX_COPIED_INSN];
|
||||
/* detour code buffer */
|
||||
kprobe_opcode_t *insn;
|
||||
/*
|
||||
* We always copy one instruction on ARM,
|
||||
* so size will always be 4, and unlike x86, there is no
|
||||
* need for a size field.
|
||||
*/
|
||||
};
|
||||
|
||||
#endif /* _ARM_KPROBES_H */
|
||||
|
@ -19,6 +19,8 @@
|
||||
#ifndef _ASM_PROBES_H
|
||||
#define _ASM_PROBES_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
typedef u32 probes_opcode_t;
|
||||
|
||||
struct arch_probes_insn;
|
||||
@ -38,6 +40,19 @@ struct arch_probes_insn {
|
||||
probes_check_cc *insn_check_cc;
|
||||
probes_insn_singlestep_t *insn_singlestep;
|
||||
probes_insn_fn_t *insn_fn;
|
||||
int stack_space;
|
||||
unsigned long register_usage_flags;
|
||||
bool kprobe_direct_exec;
|
||||
};
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
/*
|
||||
* We assume one instruction can consume at most 64 bytes stack, which is
|
||||
* 'push {r0-r15}'. Instructions consume more or unknown stack space like
|
||||
* 'str r0, [sp, #-80]' and 'str r0, [sp, r1]' should be prohibit to probe.
|
||||
* Both kprobe and jprobe use this macro.
|
||||
*/
|
||||
#define MAX_STACK_SIZE 64
|
||||
|
||||
#endif
|
||||
|
@ -51,20 +51,8 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o insn.o
|
||||
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o insn.o
|
||||
obj-$(CONFIG_JUMP_LABEL) += jump_label.o insn.o patch.o
|
||||
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
|
||||
obj-$(CONFIG_UPROBES) += probes.o probes-arm.o uprobes.o uprobes-arm.o
|
||||
obj-$(CONFIG_KPROBES) += probes.o kprobes.o kprobes-common.o patch.o
|
||||
ifdef CONFIG_THUMB2_KERNEL
|
||||
obj-$(CONFIG_KPROBES) += kprobes-thumb.o probes-thumb.o
|
||||
else
|
||||
obj-$(CONFIG_KPROBES) += kprobes-arm.o probes-arm.o
|
||||
endif
|
||||
obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o
|
||||
test-kprobes-objs := kprobes-test.o
|
||||
ifdef CONFIG_THUMB2_KERNEL
|
||||
test-kprobes-objs += kprobes-test-thumb.o
|
||||
else
|
||||
test-kprobes-objs += kprobes-test-arm.o
|
||||
endif
|
||||
# Main staffs in KPROBES are in arch/arm/probes/ .
|
||||
obj-$(CONFIG_KPROBES) += patch.o insn.o
|
||||
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
|
||||
obj-$(CONFIG_ARM_THUMBEE) += thumbee.o
|
||||
obj-$(CONFIG_KGDB) += kgdb.o patch.o
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "entry-header.S"
|
||||
#include <asm/entry-macro-multi.S>
|
||||
#include <asm/probes.h>
|
||||
|
||||
/*
|
||||
* Interrupt handling.
|
||||
@ -249,7 +250,7 @@ __und_svc:
|
||||
@ If a kprobe is about to simulate a "stmdb sp..." instruction,
|
||||
@ it obviously needs free stack space which then will belong to
|
||||
@ the saved context.
|
||||
svc_entry 64
|
||||
svc_entry MAX_STACK_SIZE
|
||||
#else
|
||||
svc_entry
|
||||
#endif
|
||||
|
@ -20,8 +20,7 @@
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/opcodes.h>
|
||||
#include <asm/ftrace.h>
|
||||
|
||||
#include "insn.h"
|
||||
#include <asm/insn.h>
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
#define NOP 0xf85deb04 /* pop.w {lr} */
|
||||
|
@ -1,8 +1,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
#include "insn.h"
|
||||
#include "patch.h"
|
||||
#include <asm/patch.h>
|
||||
#include <asm/insn.h>
|
||||
|
||||
#ifdef HAVE_JUMP_LABEL
|
||||
|
||||
|
@ -14,10 +14,9 @@
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/patch.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
#include "patch.h"
|
||||
|
||||
struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
|
||||
{
|
||||
{ "r0", 4, offsetof(struct pt_regs, ARM_r0)},
|
||||
|
@ -8,8 +8,7 @@
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/opcodes.h>
|
||||
|
||||
#include "patch.h"
|
||||
#include <asm/patch.h>
|
||||
|
||||
struct patch {
|
||||
void *addr;
|
||||
|
@ -1049,6 +1049,15 @@ static int c_show(struct seq_file *m, void *v)
|
||||
seq_printf(m, "model name\t: %s rev %d (%s)\n",
|
||||
cpu_name, cpuid & 15, elf_platform);
|
||||
|
||||
#if defined(CONFIG_SMP)
|
||||
seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
|
||||
per_cpu(cpu_data, i).loops_per_jiffy / (500000UL/HZ),
|
||||
(per_cpu(cpu_data, i).loops_per_jiffy / (5000UL/HZ)) % 100);
|
||||
#else
|
||||
seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
|
||||
loops_per_jiffy / (500000/HZ),
|
||||
(loops_per_jiffy / (5000/HZ)) % 100);
|
||||
#endif
|
||||
/* dump out the processor features */
|
||||
seq_puts(m, "Features\t: ");
|
||||
|
||||
|
@ -387,6 +387,18 @@ asmlinkage void secondary_start_kernel(void)
|
||||
|
||||
void __init smp_cpus_done(unsigned int max_cpus)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long bogosum = 0;
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
bogosum += per_cpu(cpu_data, cpu).loops_per_jiffy;
|
||||
|
||||
printk(KERN_INFO "SMP: Total of %d processors activated "
|
||||
"(%lu.%02lu BogoMIPS).\n",
|
||||
num_online_cpus(),
|
||||
bogosum / (500000/HZ),
|
||||
(bogosum / (5000/HZ)) % 100);
|
||||
|
||||
hyp_mode_check();
|
||||
}
|
||||
|
||||
|
7
arch/arm/probes/Makefile
Normal file
7
arch/arm/probes/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
obj-$(CONFIG_UPROBES) += decode.o decode-arm.o uprobes/
|
||||
obj-$(CONFIG_KPROBES) += decode.o kprobes/
|
||||
ifdef CONFIG_THUMB2_KERNEL
|
||||
obj-$(CONFIG_KPROBES) += decode-thumb.o
|
||||
else
|
||||
obj-$(CONFIG_KPROBES) += decode-arm.o
|
||||
endif
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes-arm.c
|
||||
*
|
||||
* arch/arm/probes/decode-arm.c
|
||||
*
|
||||
* Some code moved here from arch/arm/kernel/kprobes-arm.c
|
||||
*
|
||||
@ -20,8 +21,8 @@
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "decode.h"
|
||||
#include "decode-arm.h"
|
||||
|
||||
#define sign_extend(x, signbit) ((x) | (0 - ((x) & (1 << (signbit)))))
|
||||
|
||||
@ -369,17 +370,17 @@ static const union decode_item arm_cccc_001x_table[] = {
|
||||
|
||||
/* MOVW cccc 0011 0000 xxxx xxxx xxxx xxxx xxxx */
|
||||
/* MOVT cccc 0011 0100 xxxx xxxx xxxx xxxx xxxx */
|
||||
DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_DATA_PROCESSING_IMM,
|
||||
DECODE_EMULATEX (0x0fb00000, 0x03000000, PROBES_MOV_HALFWORD,
|
||||
REGS(0, NOPC, 0, 0, 0)),
|
||||
|
||||
/* YIELD cccc 0011 0010 0000 xxxx xxxx 0000 0001 */
|
||||
DECODE_OR (0x0fff00ff, 0x03200001),
|
||||
/* SEV cccc 0011 0010 0000 xxxx xxxx 0000 0100 */
|
||||
DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_EMULATE_NONE),
|
||||
DECODE_EMULATE (0x0fff00ff, 0x03200004, PROBES_SEV),
|
||||
/* NOP cccc 0011 0010 0000 xxxx xxxx 0000 0000 */
|
||||
/* WFE cccc 0011 0010 0000 xxxx xxxx 0000 0010 */
|
||||
/* WFI cccc 0011 0010 0000 xxxx xxxx 0000 0011 */
|
||||
DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_SIMULATE_NOP),
|
||||
DECODE_SIMULATE (0x0fff00fc, 0x03200000, PROBES_WFE),
|
||||
/* DBG cccc 0011 0010 0000 xxxx xxxx ffff xxxx */
|
||||
/* unallocated hints cccc 0011 0010 0000 xxxx xxxx xxxx xxxx */
|
||||
/* MSR (immediate) cccc 0011 0x10 xxxx xxxx xxxx xxxx xxxx */
|
||||
@ -725,10 +726,11 @@ static void __kprobes arm_singlestep(probes_opcode_t insn,
|
||||
*/
|
||||
enum probes_insn __kprobes
|
||||
arm_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions)
|
||||
bool emulate, const union decode_action *actions,
|
||||
const struct decode_checker *checkers[])
|
||||
{
|
||||
asi->insn_singlestep = arm_singlestep;
|
||||
asi->insn_check_cc = probes_condition_checks[insn>>28];
|
||||
return probes_decode_insn(insn, asi, probes_decode_arm_table, false,
|
||||
emulate, actions);
|
||||
emulate, actions, checkers);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes-arm.h
|
||||
* arch/arm/probes/decode-arm.h
|
||||
*
|
||||
* Copyright 2013 Linaro Ltd.
|
||||
* Written by: David A. Long
|
||||
@ -15,9 +15,9 @@
|
||||
#ifndef _ARM_KERNEL_PROBES_ARM_H
|
||||
#define _ARM_KERNEL_PROBES_ARM_H
|
||||
|
||||
#include "decode.h"
|
||||
|
||||
enum probes_arm_action {
|
||||
PROBES_EMULATE_NONE,
|
||||
PROBES_SIMULATE_NOP,
|
||||
PROBES_PRELOAD_IMM,
|
||||
PROBES_PRELOAD_REG,
|
||||
PROBES_BRANCH_IMM,
|
||||
@ -68,6 +68,7 @@ extern const union decode_item probes_decode_arm_table[];
|
||||
|
||||
enum probes_insn arm_probes_decode_insn(probes_opcode_t,
|
||||
struct arch_probes_insn *, bool emulate,
|
||||
const union decode_action *actions);
|
||||
const union decode_action *actions,
|
||||
const struct decode_checker *checkers[]);
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes-thumb.c
|
||||
* arch/arm/probes/decode-thumb.c
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
@ -12,8 +12,8 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "probes-thumb.h"
|
||||
#include "decode.h"
|
||||
#include "decode-thumb.h"
|
||||
|
||||
|
||||
static const union decode_item t32_table_1110_100x_x0xx[] = {
|
||||
@ -863,20 +863,22 @@ static void __kprobes thumb32_singlestep(probes_opcode_t opcode,
|
||||
|
||||
enum probes_insn __kprobes
|
||||
thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions)
|
||||
bool emulate, const union decode_action *actions,
|
||||
const struct decode_checker *checkers[])
|
||||
{
|
||||
asi->insn_singlestep = thumb16_singlestep;
|
||||
asi->insn_check_cc = thumb_check_cc;
|
||||
return probes_decode_insn(insn, asi, probes_decode_thumb16_table, true,
|
||||
emulate, actions);
|
||||
emulate, actions, checkers);
|
||||
}
|
||||
|
||||
enum probes_insn __kprobes
|
||||
thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions)
|
||||
bool emulate, const union decode_action *actions,
|
||||
const struct decode_checker *checkers[])
|
||||
{
|
||||
asi->insn_singlestep = thumb32_singlestep;
|
||||
asi->insn_check_cc = thumb_check_cc;
|
||||
return probes_decode_insn(insn, asi, probes_decode_thumb32_table, true,
|
||||
emulate, actions);
|
||||
emulate, actions, checkers);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes-thumb.h
|
||||
* arch/arm/probes/decode-thumb.h
|
||||
*
|
||||
* Copyright 2013 Linaro Ltd.
|
||||
* Written by: David A. Long
|
||||
@ -15,6 +15,8 @@
|
||||
#ifndef _ARM_KERNEL_PROBES_THUMB_H
|
||||
#define _ARM_KERNEL_PROBES_THUMB_H
|
||||
|
||||
#include "decode.h"
|
||||
|
||||
/*
|
||||
* True if current instruction is in an IT block.
|
||||
*/
|
||||
@ -89,9 +91,11 @@ extern const union decode_item probes_decode_thumb16_table[];
|
||||
|
||||
enum probes_insn __kprobes
|
||||
thumb16_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions);
|
||||
bool emulate, const union decode_action *actions,
|
||||
const struct decode_checker *checkers[]);
|
||||
enum probes_insn __kprobes
|
||||
thumb32_probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
bool emulate, const union decode_action *actions);
|
||||
bool emulate, const union decode_action *actions,
|
||||
const struct decode_checker *checkers[]);
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes.c
|
||||
* arch/arm/probes/decode.c
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
@ -17,7 +17,7 @@
|
||||
#include <asm/ptrace.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "decode.h"
|
||||
|
||||
|
||||
#ifndef find_str_pc_offset
|
||||
@ -342,6 +342,31 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
|
||||
[DECODE_TYPE_REJECT] = sizeof(struct decode_reject)
|
||||
};
|
||||
|
||||
static int run_checkers(const struct decode_checker *checkers[],
|
||||
int action, probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
const struct decode_checker **p;
|
||||
|
||||
if (!checkers)
|
||||
return INSN_GOOD;
|
||||
|
||||
p = checkers;
|
||||
while (*p != NULL) {
|
||||
int retval;
|
||||
probes_check_t *checker_func = (*p)[action].checker;
|
||||
|
||||
retval = INSN_GOOD;
|
||||
if (checker_func)
|
||||
retval = checker_func(insn, asi, h);
|
||||
if (retval == INSN_REJECTED)
|
||||
return retval;
|
||||
p++;
|
||||
}
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* probes_decode_insn operates on data tables in order to decode an ARM
|
||||
* architecture instruction onto which a kprobe has been placed.
|
||||
@ -388,11 +413,34 @@ static const int decode_struct_sizes[NUM_DECODE_TYPES] = {
|
||||
int __kprobes
|
||||
probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const union decode_item *table, bool thumb,
|
||||
bool emulate, const union decode_action *actions)
|
||||
bool emulate, const union decode_action *actions,
|
||||
const struct decode_checker *checkers[])
|
||||
{
|
||||
const struct decode_header *h = (struct decode_header *)table;
|
||||
const struct decode_header *next;
|
||||
bool matched = false;
|
||||
/*
|
||||
* @insn can be modified by decode_regs. Save its original
|
||||
* value for checkers.
|
||||
*/
|
||||
probes_opcode_t origin_insn = insn;
|
||||
|
||||
/*
|
||||
* stack_space is initialized to 0 here. Checker functions
|
||||
* should update is value if they find this is a stack store
|
||||
* instruction: positive value means bytes of stack usage,
|
||||
* negitive value means unable to determine stack usage
|
||||
* statically. For instruction doesn't store to stack, checker
|
||||
* do nothing with it.
|
||||
*/
|
||||
asi->stack_space = 0;
|
||||
|
||||
/*
|
||||
* Similarly to stack_space, register_usage_flags is filled by
|
||||
* checkers. Its default value is set to ~0, which is 'all
|
||||
* registers are used', to prevent any potential optimization.
|
||||
*/
|
||||
asi->register_usage_flags = ~0UL;
|
||||
|
||||
if (emulate)
|
||||
insn = prepare_emulated_insn(insn, asi, thumb);
|
||||
@ -422,24 +470,41 @@ probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
}
|
||||
|
||||
case DECODE_TYPE_CUSTOM: {
|
||||
int err;
|
||||
struct decode_custom *d = (struct decode_custom *)h;
|
||||
return actions[d->decoder.action].decoder(insn, asi, h);
|
||||
int action = d->decoder.action;
|
||||
|
||||
err = run_checkers(checkers, action, origin_insn, asi, h);
|
||||
if (err == INSN_REJECTED)
|
||||
return INSN_REJECTED;
|
||||
return actions[action].decoder(insn, asi, h);
|
||||
}
|
||||
|
||||
case DECODE_TYPE_SIMULATE: {
|
||||
int err;
|
||||
struct decode_simulate *d = (struct decode_simulate *)h;
|
||||
asi->insn_handler = actions[d->handler.action].handler;
|
||||
int action = d->handler.action;
|
||||
|
||||
err = run_checkers(checkers, action, origin_insn, asi, h);
|
||||
if (err == INSN_REJECTED)
|
||||
return INSN_REJECTED;
|
||||
asi->insn_handler = actions[action].handler;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
case DECODE_TYPE_EMULATE: {
|
||||
int err;
|
||||
struct decode_emulate *d = (struct decode_emulate *)h;
|
||||
int action = d->handler.action;
|
||||
|
||||
err = run_checkers(checkers, action, origin_insn, asi, h);
|
||||
if (err == INSN_REJECTED)
|
||||
return INSN_REJECTED;
|
||||
|
||||
if (!emulate)
|
||||
return actions[d->handler.action].decoder(insn,
|
||||
asi, h);
|
||||
return actions[action].decoder(insn, asi, h);
|
||||
|
||||
asi->insn_handler = actions[d->handler.action].handler;
|
||||
asi->insn_handler = actions[action].handler;
|
||||
set_emulated_insn(insn, asi, thumb);
|
||||
return INSN_GOOD;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/probes.h
|
||||
* arch/arm/probes/decode.h
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
@ -314,6 +314,14 @@ union decode_action {
|
||||
probes_custom_decode_t *decoder;
|
||||
};
|
||||
|
||||
typedef enum probes_insn (probes_check_t)(probes_opcode_t,
|
||||
struct arch_probes_insn *,
|
||||
const struct decode_header *);
|
||||
|
||||
struct decode_checker {
|
||||
probes_check_t *checker;
|
||||
};
|
||||
|
||||
#define DECODE_END \
|
||||
{.bits = DECODE_TYPE_END}
|
||||
|
||||
@ -402,6 +410,7 @@ probes_insn_handler_t probes_emulate_none;
|
||||
int __kprobes
|
||||
probes_decode_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const union decode_item *table, bool thumb, bool emulate,
|
||||
const union decode_action *actions);
|
||||
const union decode_action *actions,
|
||||
const struct decode_checker **checkers);
|
||||
|
||||
#endif
|
12
arch/arm/probes/kprobes/Makefile
Normal file
12
arch/arm/probes/kprobes/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
obj-$(CONFIG_KPROBES) += core.o actions-common.o checkers-common.o
|
||||
obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o
|
||||
test-kprobes-objs := test-core.o
|
||||
|
||||
ifdef CONFIG_THUMB2_KERNEL
|
||||
obj-$(CONFIG_KPROBES) += actions-thumb.o checkers-thumb.o
|
||||
test-kprobes-objs += test-thumb.o
|
||||
else
|
||||
obj-$(CONFIG_KPROBES) += actions-arm.o checkers-arm.o
|
||||
obj-$(CONFIG_OPTPROBES) += opt-arm.o
|
||||
test-kprobes-objs += test-arm.o
|
||||
endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes-decode.c
|
||||
* arch/arm/probes/kprobes/actions-arm.c
|
||||
*
|
||||
* Copyright (C) 2006, 2007 Motorola Inc.
|
||||
*
|
||||
@ -62,8 +62,9 @@
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include "kprobes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "../decode-arm.h"
|
||||
#include "core.h"
|
||||
#include "checkers.h"
|
||||
|
||||
#if __LINUX_ARM_ARCH__ >= 6
|
||||
#define BLX(reg) "blx "reg" \n\t"
|
||||
@ -302,8 +303,6 @@ emulate_rdlo12rdhi16rn0rm8_rwflags_nopc(probes_opcode_t insn,
|
||||
}
|
||||
|
||||
const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = {
|
||||
[PROBES_EMULATE_NONE] = {.handler = probes_emulate_none},
|
||||
[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
|
||||
[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
|
||||
[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
|
||||
[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
|
||||
@ -341,3 +340,5 @@ const union decode_action kprobes_arm_actions[NUM_PROBES_ARM_ACTIONS] = {
|
||||
[PROBES_BRANCH] = {.handler = simulate_bbl},
|
||||
[PROBES_LDMSTM] = {.decoder = kprobe_decode_ldmstm}
|
||||
};
|
||||
|
||||
const struct decode_checker *kprobes_arm_checkers[] = {arm_stack_checker, arm_regs_checker, NULL};
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes-common.c
|
||||
* arch/arm/probes/kprobes/actions-common.c
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
@ -15,7 +15,7 @@
|
||||
#include <linux/kprobes.h>
|
||||
#include <asm/opcodes.h>
|
||||
|
||||
#include "kprobes.h"
|
||||
#include "core.h"
|
||||
|
||||
|
||||
static void __kprobes simulate_ldm1stm1(probes_opcode_t insn,
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes-thumb.c
|
||||
* arch/arm/probes/kprobes/actions-thumb.c
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
@ -13,8 +13,9 @@
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#include "kprobes.h"
|
||||
#include "probes-thumb.h"
|
||||
#include "../decode-thumb.h"
|
||||
#include "core.h"
|
||||
#include "checkers.h"
|
||||
|
||||
/* These emulation encodings are functionally equivalent... */
|
||||
#define t32_emulate_rd8rn16rm0ra12_noflags \
|
||||
@ -664,3 +665,6 @@ const union decode_action kprobes_t32_actions[NUM_PROBES_T32_ACTIONS] = {
|
||||
[PROBES_T32_MUL_ADD_LONG] = {
|
||||
.handler = t32_emulate_rdlo12rdhi8rn16rm0_noflags},
|
||||
};
|
||||
|
||||
const struct decode_checker *kprobes_t32_checkers[] = {t32_stack_checker, NULL};
|
||||
const struct decode_checker *kprobes_t16_checkers[] = {t16_stack_checker, NULL};
|
192
arch/arm/probes/kprobes/checkers-arm.c
Normal file
192
arch/arm/probes/kprobes/checkers-arm.c
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* arch/arm/probes/kprobes/checkers-arm.c
|
||||
*
|
||||
* Copyright (C) 2014 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "../decode.h"
|
||||
#include "../decode-arm.h"
|
||||
#include "checkers.h"
|
||||
|
||||
static enum probes_insn __kprobes arm_check_stack(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
/*
|
||||
* PROBES_LDRSTRD, PROBES_LDMSTM, PROBES_STORE,
|
||||
* PROBES_STORE_EXTRA may get here. Simply mark all normal
|
||||
* insns as STACK_USE_NONE.
|
||||
*/
|
||||
static const union decode_item table[] = {
|
||||
/*
|
||||
* 'STR{,D,B,H}, Rt, [Rn, Rm]' should be marked as UNKNOWN
|
||||
* if Rn or Rm is SP.
|
||||
* x
|
||||
* STR (register) cccc 011x x0x0 xxxx xxxx xxxx xxxx xxxx
|
||||
* STRB (register) cccc 011x x1x0 xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_OR (0x0e10000f, 0x0600000d),
|
||||
DECODE_OR (0x0e1f0000, 0x060d0000),
|
||||
|
||||
/*
|
||||
* x
|
||||
* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx
|
||||
* STRH (register) cccc 000x x0x0 xxxx xxxx xxxx 1011 xxxx
|
||||
*/
|
||||
DECODE_OR (0x0e5000bf, 0x000000bd),
|
||||
DECODE_CUSTOM (0x0e5f00b0, 0x000d00b0, STACK_USE_UNKNOWN),
|
||||
|
||||
/*
|
||||
* For PROBES_LDMSTM, only stmdx sp, [...] need to examine
|
||||
*
|
||||
* Bit B/A (bit 24) encodes arithmetic operation order. 1 means
|
||||
* before, 0 means after.
|
||||
* Bit I/D (bit 23) encodes arithmetic operation. 1 means
|
||||
* increment, 0 means decrement.
|
||||
*
|
||||
* So:
|
||||
* B I
|
||||
* / /
|
||||
* A D | Rn |
|
||||
* STMDX SP, [...] cccc 100x 00x0 xxxx xxxx xxxx xxxx xxxx
|
||||
*/
|
||||
DECODE_CUSTOM (0x0edf0000, 0x080d0000, STACK_USE_STMDX),
|
||||
|
||||
/* P U W | Rn | Rt | imm12 |*/
|
||||
/* STR (immediate) cccc 010x x0x0 1101 xxxx xxxx xxxx xxxx */
|
||||
/* STRB (immediate) cccc 010x x1x0 1101 xxxx xxxx xxxx xxxx */
|
||||
/* P U W | Rn | Rt |imm4| |imm4|*/
|
||||
/* STRD (immediate) cccc 000x x1x0 1101 xxxx xxxx 1111 xxxx */
|
||||
/* STRH (immediate) cccc 000x x1x0 1101 xxxx xxxx 1011 xxxx */
|
||||
/*
|
||||
* index = (P == '1'); add = (U == '1').
|
||||
* Above insns with:
|
||||
* index == 0 (str{,d,h} rx, [sp], #+/-imm) or
|
||||
* add == 1 (str{,d,h} rx, [sp, #+<imm>])
|
||||
* should be STACK_USE_NONE.
|
||||
* Only str{,b,d,h} rx,[sp,#-n] (P == 1 and U == 0) are
|
||||
* required to be examined.
|
||||
*/
|
||||
/* STR{,B} Rt,[SP,#-n] cccc 0101 0xx0 1101 xxxx xxxx xxxx xxxx */
|
||||
DECODE_CUSTOM (0x0f9f0000, 0x050d0000, STACK_USE_FIXED_XXX),
|
||||
|
||||
/* STR{D,H} Rt,[SP,#-n] cccc 0001 01x0 1101 xxxx xxxx 1x11 xxxx */
|
||||
DECODE_CUSTOM (0x0fdf00b0, 0x014d00b0, STACK_USE_FIXED_X0X),
|
||||
|
||||
/* fall through */
|
||||
DECODE_CUSTOM (0, 0, STACK_USE_NONE),
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL);
|
||||
}
|
||||
|
||||
const struct decode_checker arm_stack_checker[NUM_PROBES_ARM_ACTIONS] = {
|
||||
[PROBES_LDRSTRD] = {.checker = arm_check_stack},
|
||||
[PROBES_STORE_EXTRA] = {.checker = arm_check_stack},
|
||||
[PROBES_STORE] = {.checker = arm_check_stack},
|
||||
[PROBES_LDMSTM] = {.checker = arm_check_stack},
|
||||
};
|
||||
|
||||
static enum probes_insn __kprobes arm_check_regs_nouse(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
asi->register_usage_flags = 0;
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
static enum probes_insn arm_check_regs_normal(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
u32 regs = h->type_regs.bits >> DECODE_TYPE_BITS;
|
||||
int i;
|
||||
|
||||
asi->register_usage_flags = 0;
|
||||
for (i = 0; i < 5; regs >>= 4, insn >>= 4, i++)
|
||||
if (regs & 0xf)
|
||||
asi->register_usage_flags |= 1 << (insn & 0xf);
|
||||
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
|
||||
static enum probes_insn arm_check_regs_ldmstm(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
unsigned int reglist = insn & 0xffff;
|
||||
unsigned int rn = (insn >> 16) & 0xf;
|
||||
asi->register_usage_flags = reglist | (1 << rn);
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
static enum probes_insn arm_check_regs_mov_ip_sp(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
/* Instruction is 'mov ip, sp' i.e. 'mov r12, r13' */
|
||||
asi->register_usage_flags = (1 << 12) | (1<< 13);
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* | Rn |Rt/d| | Rm |
|
||||
* LDRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1101 xxxx
|
||||
* STRD (register) cccc 000x x0x0 xxxx xxxx xxxx 1111 xxxx
|
||||
* | Rn |Rt/d| |imm4L|
|
||||
* LDRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1101 xxxx
|
||||
* STRD (immediate) cccc 000x x1x0 xxxx xxxx xxxx 1111 xxxx
|
||||
*
|
||||
* Such instructions access Rt/d and its next register, so different
|
||||
* from others, a specific checker is required to handle this extra
|
||||
* implicit register usage.
|
||||
*/
|
||||
static enum probes_insn arm_check_regs_ldrdstrd(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
int rdt = (insn >> 12) & 0xf;
|
||||
arm_check_regs_normal(insn, asi, h);
|
||||
asi->register_usage_flags |= 1 << (rdt + 1);
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
|
||||
const struct decode_checker arm_regs_checker[NUM_PROBES_ARM_ACTIONS] = {
|
||||
[PROBES_MRS] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_SATURATING_ARITHMETIC] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_MUL1] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_MUL2] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_MUL_ADD_LONG] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_MUL_ADD] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_LOAD] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_LOAD_EXTRA] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_STORE] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_STORE_EXTRA] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_DATA_PROCESSING_REG] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_DATA_PROCESSING_IMM] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_SEV] = {.checker = arm_check_regs_nouse},
|
||||
[PROBES_WFE] = {.checker = arm_check_regs_nouse},
|
||||
[PROBES_SATURATE] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_REV] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_MMI] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_PACK] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_EXTEND] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_EXTEND_ADD] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_BITFIELD] = {.checker = arm_check_regs_normal},
|
||||
[PROBES_LDMSTM] = {.checker = arm_check_regs_ldmstm},
|
||||
[PROBES_MOV_IP_SP] = {.checker = arm_check_regs_mov_ip_sp},
|
||||
[PROBES_LDRSTRD] = {.checker = arm_check_regs_ldrdstrd},
|
||||
};
|
101
arch/arm/probes/kprobes/checkers-common.c
Normal file
101
arch/arm/probes/kprobes/checkers-common.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* arch/arm/probes/kprobes/checkers-common.c
|
||||
*
|
||||
* Copyright (C) 2014 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "../decode.h"
|
||||
#include "../decode-arm.h"
|
||||
#include "checkers.h"
|
||||
|
||||
enum probes_insn checker_stack_use_none(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
asi->stack_space = 0;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
enum probes_insn checker_stack_use_unknown(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
asi->stack_space = -1;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
enum probes_insn checker_stack_use_imm_0xx(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
int imm = insn & 0xff;
|
||||
asi->stack_space = imm;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Different from other insn uses imm8, the real addressing offset of
|
||||
* STRD in T32 encoding should be imm8 * 4. See ARMARM description.
|
||||
*/
|
||||
enum probes_insn checker_stack_use_t32strd(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
int imm = insn & 0xff;
|
||||
asi->stack_space = imm << 2;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
#else
|
||||
enum probes_insn checker_stack_use_imm_x0x(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
int imm = ((insn & 0xf00) >> 4) + (insn & 0xf);
|
||||
asi->stack_space = imm;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum probes_insn checker_stack_use_imm_xxx(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
int imm = insn & 0xfff;
|
||||
asi->stack_space = imm;
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
enum probes_insn checker_stack_use_stmdx(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
unsigned int reglist = insn & 0xffff;
|
||||
int pbit = insn & (1 << 24);
|
||||
asi->stack_space = (hweight32(reglist) - (!pbit ? 1 : 0)) * 4;
|
||||
|
||||
return INSN_GOOD_NO_SLOT;
|
||||
}
|
||||
|
||||
const union decode_action stack_check_actions[] = {
|
||||
[STACK_USE_NONE] = {.decoder = checker_stack_use_none},
|
||||
[STACK_USE_UNKNOWN] = {.decoder = checker_stack_use_unknown},
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
[STACK_USE_FIXED_0XX] = {.decoder = checker_stack_use_imm_0xx},
|
||||
[STACK_USE_T32STRD] = {.decoder = checker_stack_use_t32strd},
|
||||
#else
|
||||
[STACK_USE_FIXED_X0X] = {.decoder = checker_stack_use_imm_x0x},
|
||||
#endif
|
||||
[STACK_USE_FIXED_XXX] = {.decoder = checker_stack_use_imm_xxx},
|
||||
[STACK_USE_STMDX] = {.decoder = checker_stack_use_stmdx},
|
||||
};
|
110
arch/arm/probes/kprobes/checkers-thumb.c
Normal file
110
arch/arm/probes/kprobes/checkers-thumb.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* arch/arm/probes/kprobes/checkers-thumb.c
|
||||
*
|
||||
* Copyright (C) 2014 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "../decode.h"
|
||||
#include "../decode-thumb.h"
|
||||
#include "checkers.h"
|
||||
|
||||
static enum probes_insn __kprobes t32_check_stack(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
/*
|
||||
* PROBES_T32_LDMSTM, PROBES_T32_LDRDSTRD and PROBES_T32_LDRSTR
|
||||
* may get here. Simply mark all normal insns as STACK_USE_NONE.
|
||||
*/
|
||||
static const union decode_item table[] = {
|
||||
|
||||
/*
|
||||
* First, filter out all ldr insns to make our life easier.
|
||||
* Following load insns may come here:
|
||||
* LDM, LDRD, LDR.
|
||||
* In T32 encoding, bit 20 is enough for distinguishing
|
||||
* load and store. All load insns have this bit set, when
|
||||
* all store insns have this bit clear.
|
||||
*/
|
||||
DECODE_CUSTOM (0x00100000, 0x00100000, STACK_USE_NONE),
|
||||
|
||||
/*
|
||||
* Mark all 'STR{,B,H}, Rt, [Rn, Rm]' as STACK_USE_UNKNOWN
|
||||
* if Rn or Rm is SP. T32 doesn't encode STRD.
|
||||
*/
|
||||
/* xx | Rn | Rt | | Rm |*/
|
||||
/* STR (register) 1111 1000 0100 xxxx xxxx 0000 00xx xxxx */
|
||||
/* STRB (register) 1111 1000 0000 xxxx xxxx 0000 00xx xxxx */
|
||||
/* STRH (register) 1111 1000 0010 xxxx xxxx 0000 00xx xxxx */
|
||||
/* INVALID INSN 1111 1000 0110 xxxx xxxx 0000 00xx xxxx */
|
||||
/* By Introducing INVALID INSN, bit 21 and 22 can be ignored. */
|
||||
DECODE_OR (0xff9f0fc0, 0xf80d0000),
|
||||
DECODE_CUSTOM (0xff900fcf, 0xf800000d, STACK_USE_UNKNOWN),
|
||||
|
||||
|
||||
/* xx | Rn | Rt | PUW| imm8 |*/
|
||||
/* STR (imm 8) 1111 1000 0100 1101 xxxx 110x xxxx xxxx */
|
||||
/* STRB (imm 8) 1111 1000 0000 1101 xxxx 110x xxxx xxxx */
|
||||
/* STRH (imm 8) 1111 1000 0010 1101 xxxx 110x xxxx xxxx */
|
||||
/* INVALID INSN 1111 1000 0110 1101 xxxx 110x xxxx xxxx */
|
||||
/* Only consider U == 0 and P == 1: strx rx, [sp, #-<imm>] */
|
||||
DECODE_CUSTOM (0xff9f0e00, 0xf80d0c00, STACK_USE_FIXED_0XX),
|
||||
|
||||
/* For STR{,B,H} (imm 12), offset is always positive, so ignore them. */
|
||||
|
||||
/* P U W | Rn | Rt | Rt2| imm8 |*/
|
||||
/* STRD (immediate) 1110 1001 01x0 1101 xxxx xxxx xxxx xxxx */
|
||||
/*
|
||||
* Only consider U == 0 and P == 1.
|
||||
* Also note that STRD in T32 encoding is special:
|
||||
* imm = ZeroExtend(imm8:'00', 32)
|
||||
*/
|
||||
DECODE_CUSTOM (0xffdf0000, 0xe94d0000, STACK_USE_T32STRD),
|
||||
|
||||
/* | Rn | */
|
||||
/* STMDB 1110 1001 00x0 1101 xxxx xxxx xxxx xxxx */
|
||||
DECODE_CUSTOM (0xffdf0000, 0xe90d0000, STACK_USE_STMDX),
|
||||
|
||||
/* fall through */
|
||||
DECODE_CUSTOM (0, 0, STACK_USE_NONE),
|
||||
DECODE_END
|
||||
};
|
||||
|
||||
return probes_decode_insn(insn, asi, table, false, false, stack_check_actions, NULL);
|
||||
}
|
||||
|
||||
const struct decode_checker t32_stack_checker[NUM_PROBES_T32_ACTIONS] = {
|
||||
[PROBES_T32_LDMSTM] = {.checker = t32_check_stack},
|
||||
[PROBES_T32_LDRDSTRD] = {.checker = t32_check_stack},
|
||||
[PROBES_T32_LDRSTR] = {.checker = t32_check_stack},
|
||||
};
|
||||
|
||||
/*
|
||||
* See following comments. This insn must be 'push'.
|
||||
*/
|
||||
static enum probes_insn __kprobes t16_check_stack(probes_opcode_t insn,
|
||||
struct arch_probes_insn *asi,
|
||||
const struct decode_header *h)
|
||||
{
|
||||
unsigned int reglist = insn & 0x1ff;
|
||||
asi->stack_space = hweight32(reglist) * 4;
|
||||
return INSN_GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* T16 encoding is simple: only the 'push' insn can need extra stack space.
|
||||
* Other insns, like str, can only use r0-r7 as Rn.
|
||||
*/
|
||||
const struct decode_checker t16_stack_checker[NUM_PROBES_T16_ACTIONS] = {
|
||||
[PROBES_T16_PUSH] = {.checker = t16_check_stack},
|
||||
};
|
55
arch/arm/probes/kprobes/checkers.h
Normal file
55
arch/arm/probes/kprobes/checkers.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* arch/arm/probes/kprobes/checkers.h
|
||||
*
|
||||
* Copyright (C) 2014 Huawei Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#ifndef _ARM_KERNEL_PROBES_CHECKERS_H
|
||||
#define _ARM_KERNEL_PROBES_CHECKERS_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include "../decode.h"
|
||||
|
||||
extern probes_check_t checker_stack_use_none;
|
||||
extern probes_check_t checker_stack_use_unknown;
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
extern probes_check_t checker_stack_use_imm_0xx;
|
||||
#else
|
||||
extern probes_check_t checker_stack_use_imm_x0x;
|
||||
#endif
|
||||
extern probes_check_t checker_stack_use_imm_xxx;
|
||||
extern probes_check_t checker_stack_use_stmdx;
|
||||
|
||||
enum {
|
||||
STACK_USE_NONE,
|
||||
STACK_USE_UNKNOWN,
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
STACK_USE_FIXED_0XX,
|
||||
STACK_USE_T32STRD,
|
||||
#else
|
||||
STACK_USE_FIXED_X0X,
|
||||
#endif
|
||||
STACK_USE_FIXED_XXX,
|
||||
STACK_USE_STMDX,
|
||||
NUM_STACK_USE_TYPES
|
||||
};
|
||||
|
||||
extern const union decode_action stack_check_actions[];
|
||||
|
||||
#ifndef CONFIG_THUMB2_KERNEL
|
||||
extern const struct decode_checker arm_stack_checker[];
|
||||
extern const struct decode_checker arm_regs_checker[];
|
||||
#else
|
||||
#endif
|
||||
extern const struct decode_checker t32_stack_checker[];
|
||||
extern const struct decode_checker t16_stack_checker[];
|
||||
#endif
|
@ -30,11 +30,11 @@
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/bug.h>
|
||||
#include <asm/patch.h>
|
||||
|
||||
#include "kprobes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "probes-thumb.h"
|
||||
#include "patch.h"
|
||||
#include "../decode-arm.h"
|
||||
#include "../decode-thumb.h"
|
||||
#include "core.h"
|
||||
|
||||
#define MIN_STACK_SIZE(addr) \
|
||||
min((unsigned long)MAX_STACK_SIZE, \
|
||||
@ -61,6 +61,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||
kprobe_decode_insn_t *decode_insn;
|
||||
const union decode_action *actions;
|
||||
int is;
|
||||
const struct decode_checker **checkers;
|
||||
|
||||
if (in_exception_text(addr))
|
||||
return -EINVAL;
|
||||
@ -74,9 +75,11 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||
insn = __opcode_thumb32_compose(insn, inst2);
|
||||
decode_insn = thumb32_probes_decode_insn;
|
||||
actions = kprobes_t32_actions;
|
||||
checkers = kprobes_t32_checkers;
|
||||
} else {
|
||||
decode_insn = thumb16_probes_decode_insn;
|
||||
actions = kprobes_t16_actions;
|
||||
checkers = kprobes_t16_checkers;
|
||||
}
|
||||
#else /* !CONFIG_THUMB2_KERNEL */
|
||||
thumb = false;
|
||||
@ -85,12 +88,13 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||
insn = __mem_to_opcode_arm(*p->addr);
|
||||
decode_insn = arm_probes_decode_insn;
|
||||
actions = kprobes_arm_actions;
|
||||
checkers = kprobes_arm_checkers;
|
||||
#endif
|
||||
|
||||
p->opcode = insn;
|
||||
p->ainsn.insn = tmp_insn;
|
||||
|
||||
switch ((*decode_insn)(insn, &p->ainsn, true, actions)) {
|
||||
switch ((*decode_insn)(insn, &p->ainsn, true, actions, checkers)) {
|
||||
case INSN_REJECTED: /* not supported */
|
||||
return -EINVAL;
|
||||
|
||||
@ -111,6 +115,15 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Never instrument insn like 'str r0, [sp, +/-r1]'. Also, insn likes
|
||||
* 'str r0, [sp, #-68]' should also be prohibited.
|
||||
* See __und_svc.
|
||||
*/
|
||||
if ((p->ainsn.stack_space < 0) ||
|
||||
(p->ainsn.stack_space > MAX_STACK_SIZE))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -150,19 +163,31 @@ void __kprobes arch_arm_kprobe(struct kprobe *p)
|
||||
* memory. It is also needed to atomically set the two half-words of a 32-bit
|
||||
* Thumb breakpoint.
|
||||
*/
|
||||
int __kprobes __arch_disarm_kprobe(void *p)
|
||||
struct patch {
|
||||
void *addr;
|
||||
unsigned int insn;
|
||||
};
|
||||
|
||||
static int __kprobes_remove_breakpoint(void *data)
|
||||
{
|
||||
struct kprobe *kp = p;
|
||||
void *addr = (void *)((uintptr_t)kp->addr & ~1);
|
||||
|
||||
__patch_text(addr, kp->opcode);
|
||||
|
||||
struct patch *p = data;
|
||||
__patch_text(p->addr, p->insn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __kprobes kprobes_remove_breakpoint(void *addr, unsigned int insn)
|
||||
{
|
||||
struct patch p = {
|
||||
.addr = addr,
|
||||
.insn = insn,
|
||||
};
|
||||
stop_machine(__kprobes_remove_breakpoint, &p, cpu_online_mask);
|
||||
}
|
||||
|
||||
void __kprobes arch_disarm_kprobe(struct kprobe *p)
|
||||
{
|
||||
stop_machine(__arch_disarm_kprobe, p, cpu_online_mask);
|
||||
kprobes_remove_breakpoint((void *)((uintptr_t)p->addr & ~1),
|
||||
p->opcode);
|
||||
}
|
||||
|
||||
void __kprobes arch_remove_kprobe(struct kprobe *p)
|
@ -19,7 +19,8 @@
|
||||
#ifndef _ARM_KERNEL_KPROBES_H
|
||||
#define _ARM_KERNEL_KPROBES_H
|
||||
|
||||
#include "probes.h"
|
||||
#include <asm/kprobes.h>
|
||||
#include "../decode.h"
|
||||
|
||||
/*
|
||||
* These undefined instructions must be unique and
|
||||
@ -29,6 +30,8 @@
|
||||
#define KPROBE_THUMB16_BREAKPOINT_INSTRUCTION 0xde18
|
||||
#define KPROBE_THUMB32_BREAKPOINT_INSTRUCTION 0xf7f0a018
|
||||
|
||||
extern void kprobes_remove_breakpoint(void *addr, unsigned int insn);
|
||||
|
||||
enum probes_insn __kprobes
|
||||
kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi,
|
||||
const struct decode_header *h);
|
||||
@ -36,16 +39,19 @@ kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_probes_insn *asi,
|
||||
typedef enum probes_insn (kprobe_decode_insn_t)(probes_opcode_t,
|
||||
struct arch_probes_insn *,
|
||||
bool,
|
||||
const union decode_action *);
|
||||
const union decode_action *,
|
||||
const struct decode_checker *[]);
|
||||
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
|
||||
extern const union decode_action kprobes_t32_actions[];
|
||||
extern const union decode_action kprobes_t16_actions[];
|
||||
|
||||
extern const struct decode_checker *kprobes_t32_checkers[];
|
||||
extern const struct decode_checker *kprobes_t16_checkers[];
|
||||
#else /* !CONFIG_THUMB2_KERNEL */
|
||||
|
||||
extern const union decode_action kprobes_arm_actions[];
|
||||
extern const struct decode_checker *kprobes_arm_checkers[];
|
||||
|
||||
#endif
|
||||
|
370
arch/arm/probes/kprobes/opt-arm.c
Normal file
370
arch/arm/probes/kprobes/opt-arm.c
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Kernel Probes Jump Optimization (Optprobes)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2002, 2004
|
||||
* Copyright (C) Hitachi Ltd., 2012
|
||||
* Copyright (C) Huawei Inc., 2014
|
||||
*/
|
||||
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <asm/kprobes.h>
|
||||
#include <asm/cacheflush.h>
|
||||
/* for arm_gen_branch */
|
||||
#include <asm/insn.h>
|
||||
/* for patch_text */
|
||||
#include <asm/patch.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
/*
|
||||
* See register_usage_flags. If the probed instruction doesn't use PC,
|
||||
* we can copy it into template and have it executed directly without
|
||||
* simulation or emulation.
|
||||
*/
|
||||
#define ARM_REG_PC 15
|
||||
#define can_kprobe_direct_exec(m) (!test_bit(ARM_REG_PC, &(m)))
|
||||
|
||||
/*
|
||||
* NOTE: the first sub and add instruction will be modified according
|
||||
* to the stack cost of the instruction.
|
||||
*/
|
||||
asm (
|
||||
".global optprobe_template_entry\n"
|
||||
"optprobe_template_entry:\n"
|
||||
".global optprobe_template_sub_sp\n"
|
||||
"optprobe_template_sub_sp:"
|
||||
" sub sp, sp, #0xff\n"
|
||||
" stmia sp, {r0 - r14} \n"
|
||||
".global optprobe_template_add_sp\n"
|
||||
"optprobe_template_add_sp:"
|
||||
" add r3, sp, #0xff\n"
|
||||
" str r3, [sp, #52]\n"
|
||||
" mrs r4, cpsr\n"
|
||||
" str r4, [sp, #64]\n"
|
||||
" mov r1, sp\n"
|
||||
" ldr r0, 1f\n"
|
||||
" ldr r2, 2f\n"
|
||||
/*
|
||||
* AEABI requires an 8-bytes alignment stack. If
|
||||
* SP % 8 != 0 (SP % 4 == 0 should be ensured),
|
||||
* alloc more bytes here.
|
||||
*/
|
||||
" and r4, sp, #4\n"
|
||||
" sub sp, sp, r4\n"
|
||||
#if __LINUX_ARM_ARCH__ >= 5
|
||||
" blx r2\n"
|
||||
#else
|
||||
" mov lr, pc\n"
|
||||
" mov pc, r2\n"
|
||||
#endif
|
||||
" add sp, sp, r4\n"
|
||||
" ldr r1, [sp, #64]\n"
|
||||
" tst r1, #"__stringify(PSR_T_BIT)"\n"
|
||||
" ldrne r2, [sp, #60]\n"
|
||||
" orrne r2, #1\n"
|
||||
" strne r2, [sp, #60] @ set bit0 of PC for thumb\n"
|
||||
" msr cpsr_cxsf, r1\n"
|
||||
".global optprobe_template_restore_begin\n"
|
||||
"optprobe_template_restore_begin:\n"
|
||||
" ldmia sp, {r0 - r15}\n"
|
||||
".global optprobe_template_restore_orig_insn\n"
|
||||
"optprobe_template_restore_orig_insn:\n"
|
||||
" nop\n"
|
||||
".global optprobe_template_restore_end\n"
|
||||
"optprobe_template_restore_end:\n"
|
||||
" nop\n"
|
||||
".global optprobe_template_val\n"
|
||||
"optprobe_template_val:\n"
|
||||
"1: .long 0\n"
|
||||
".global optprobe_template_call\n"
|
||||
"optprobe_template_call:\n"
|
||||
"2: .long 0\n"
|
||||
".global optprobe_template_end\n"
|
||||
"optprobe_template_end:\n");
|
||||
|
||||
#define TMPL_VAL_IDX \
|
||||
((unsigned long *)&optprobe_template_val - (unsigned long *)&optprobe_template_entry)
|
||||
#define TMPL_CALL_IDX \
|
||||
((unsigned long *)&optprobe_template_call - (unsigned long *)&optprobe_template_entry)
|
||||
#define TMPL_END_IDX \
|
||||
((unsigned long *)&optprobe_template_end - (unsigned long *)&optprobe_template_entry)
|
||||
#define TMPL_ADD_SP \
|
||||
((unsigned long *)&optprobe_template_add_sp - (unsigned long *)&optprobe_template_entry)
|
||||
#define TMPL_SUB_SP \
|
||||
((unsigned long *)&optprobe_template_sub_sp - (unsigned long *)&optprobe_template_entry)
|
||||
#define TMPL_RESTORE_BEGIN \
|
||||
((unsigned long *)&optprobe_template_restore_begin - (unsigned long *)&optprobe_template_entry)
|
||||
#define TMPL_RESTORE_ORIGN_INSN \
|
||||
((unsigned long *)&optprobe_template_restore_orig_insn - (unsigned long *)&optprobe_template_entry)
|
||||
#define TMPL_RESTORE_END \
|
||||
((unsigned long *)&optprobe_template_restore_end - (unsigned long *)&optprobe_template_entry)
|
||||
|
||||
/*
|
||||
* ARM can always optimize an instruction when using ARM ISA, except
|
||||
* instructions like 'str r0, [sp, r1]' which store to stack and unable
|
||||
* to determine stack space consumption statically.
|
||||
*/
|
||||
int arch_prepared_optinsn(struct arch_optimized_insn *optinsn)
|
||||
{
|
||||
return optinsn->insn != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* In ARM ISA, kprobe opt always replace one instruction (4 bytes
|
||||
* aligned and 4 bytes long). It is impossible to encounter another
|
||||
* kprobe in the address range. So always return 0.
|
||||
*/
|
||||
int arch_check_optimized_kprobe(struct optimized_kprobe *op)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Caller must ensure addr & 3 == 0 */
|
||||
static int can_optimize(struct kprobe *kp)
|
||||
{
|
||||
if (kp->ainsn.stack_space < 0)
|
||||
return 0;
|
||||
/*
|
||||
* 255 is the biggest imm can be used in 'sub r0, r0, #<imm>'.
|
||||
* Number larger than 255 needs special encoding.
|
||||
*/
|
||||
if (kp->ainsn.stack_space > 255 - sizeof(struct pt_regs))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Free optimized instruction slot */
|
||||
static void
|
||||
__arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
|
||||
{
|
||||
if (op->optinsn.insn) {
|
||||
free_optinsn_slot(op->optinsn.insn, dirty);
|
||||
op->optinsn.insn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
extern void kprobe_handler(struct pt_regs *regs);
|
||||
|
||||
static void
|
||||
optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct kprobe *p = &op->kp;
|
||||
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
|
||||
|
||||
/* Save skipped registers */
|
||||
regs->ARM_pc = (unsigned long)op->kp.addr;
|
||||
regs->ARM_ORIG_r0 = ~0UL;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (kprobe_running()) {
|
||||
kprobes_inc_nmissed_count(&op->kp);
|
||||
} else {
|
||||
__this_cpu_write(current_kprobe, &op->kp);
|
||||
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
|
||||
opt_pre_handler(&op->kp, regs);
|
||||
__this_cpu_write(current_kprobe, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* We singlestep the replaced instruction only when it can't be
|
||||
* executed directly during restore.
|
||||
*/
|
||||
if (!p->ainsn.kprobe_direct_exec)
|
||||
op->kp.ainsn.insn_singlestep(p->opcode, &p->ainsn, regs);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *orig)
|
||||
{
|
||||
kprobe_opcode_t *code;
|
||||
unsigned long rel_chk;
|
||||
unsigned long val;
|
||||
unsigned long stack_protect = sizeof(struct pt_regs);
|
||||
|
||||
if (!can_optimize(orig))
|
||||
return -EILSEQ;
|
||||
|
||||
code = get_optinsn_slot();
|
||||
if (!code)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Verify if the address gap is in 32MiB range, because this uses
|
||||
* a relative jump.
|
||||
*
|
||||
* kprobe opt use a 'b' instruction to branch to optinsn.insn.
|
||||
* According to ARM manual, branch instruction is:
|
||||
*
|
||||
* 31 28 27 24 23 0
|
||||
* +------+---+---+---+---+----------------+
|
||||
* | cond | 1 | 0 | 1 | 0 | imm24 |
|
||||
* +------+---+---+---+---+----------------+
|
||||
*
|
||||
* imm24 is a signed 24 bits integer. The real branch offset is computed
|
||||
* by: imm32 = SignExtend(imm24:'00', 32);
|
||||
*
|
||||
* So the maximum forward branch should be:
|
||||
* (0x007fffff << 2) = 0x01fffffc = 0x1fffffc
|
||||
* The maximum backword branch should be:
|
||||
* (0xff800000 << 2) = 0xfe000000 = -0x2000000
|
||||
*
|
||||
* We can simply check (rel & 0xfe000003):
|
||||
* if rel is positive, (rel & 0xfe000000) shoule be 0
|
||||
* if rel is negitive, (rel & 0xfe000000) should be 0xfe000000
|
||||
* the last '3' is used for alignment checking.
|
||||
*/
|
||||
rel_chk = (unsigned long)((long)code -
|
||||
(long)orig->addr + 8) & 0xfe000003;
|
||||
|
||||
if ((rel_chk != 0) && (rel_chk != 0xfe000000)) {
|
||||
/*
|
||||
* Different from x86, we free code buf directly instead of
|
||||
* calling __arch_remove_optimized_kprobe() because
|
||||
* we have not fill any field in op.
|
||||
*/
|
||||
free_optinsn_slot(code, 0);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Copy arch-dep-instance from template. */
|
||||
memcpy(code, &optprobe_template_entry,
|
||||
TMPL_END_IDX * sizeof(kprobe_opcode_t));
|
||||
|
||||
/* Adjust buffer according to instruction. */
|
||||
BUG_ON(orig->ainsn.stack_space < 0);
|
||||
|
||||
stack_protect += orig->ainsn.stack_space;
|
||||
|
||||
/* Should have been filtered by can_optimize(). */
|
||||
BUG_ON(stack_protect > 255);
|
||||
|
||||
/* Create a 'sub sp, sp, #<stack_protect>' */
|
||||
code[TMPL_SUB_SP] = __opcode_to_mem_arm(0xe24dd000 | stack_protect);
|
||||
/* Create a 'add r3, sp, #<stack_protect>' */
|
||||
code[TMPL_ADD_SP] = __opcode_to_mem_arm(0xe28d3000 | stack_protect);
|
||||
|
||||
/* Set probe information */
|
||||
val = (unsigned long)op;
|
||||
code[TMPL_VAL_IDX] = val;
|
||||
|
||||
/* Set probe function call */
|
||||
val = (unsigned long)optimized_callback;
|
||||
code[TMPL_CALL_IDX] = val;
|
||||
|
||||
/* If possible, copy insn and have it executed during restore */
|
||||
orig->ainsn.kprobe_direct_exec = false;
|
||||
if (can_kprobe_direct_exec(orig->ainsn.register_usage_flags)) {
|
||||
kprobe_opcode_t final_branch = arm_gen_branch(
|
||||
(unsigned long)(&code[TMPL_RESTORE_END]),
|
||||
(unsigned long)(op->kp.addr) + 4);
|
||||
if (final_branch != 0) {
|
||||
/*
|
||||
* Replace original 'ldmia sp, {r0 - r15}' with
|
||||
* 'ldmia {r0 - r14}', restore all registers except pc.
|
||||
*/
|
||||
code[TMPL_RESTORE_BEGIN] = __opcode_to_mem_arm(0xe89d7fff);
|
||||
|
||||
/* The original probed instruction */
|
||||
code[TMPL_RESTORE_ORIGN_INSN] = __opcode_to_mem_arm(orig->opcode);
|
||||
|
||||
/* Jump back to next instruction */
|
||||
code[TMPL_RESTORE_END] = __opcode_to_mem_arm(final_branch);
|
||||
orig->ainsn.kprobe_direct_exec = true;
|
||||
}
|
||||
}
|
||||
|
||||
flush_icache_range((unsigned long)code,
|
||||
(unsigned long)(&code[TMPL_END_IDX]));
|
||||
|
||||
/* Set op->optinsn.insn means prepared. */
|
||||
op->optinsn.insn = code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __kprobes arch_optimize_kprobes(struct list_head *oplist)
|
||||
{
|
||||
struct optimized_kprobe *op, *tmp;
|
||||
|
||||
list_for_each_entry_safe(op, tmp, oplist, list) {
|
||||
unsigned long insn;
|
||||
WARN_ON(kprobe_disabled(&op->kp));
|
||||
|
||||
/*
|
||||
* Backup instructions which will be replaced
|
||||
* by jump address
|
||||
*/
|
||||
memcpy(op->optinsn.copied_insn, op->kp.addr,
|
||||
RELATIVEJUMP_SIZE);
|
||||
|
||||
insn = arm_gen_branch((unsigned long)op->kp.addr,
|
||||
(unsigned long)op->optinsn.insn);
|
||||
BUG_ON(insn == 0);
|
||||
|
||||
/*
|
||||
* Make it a conditional branch if replaced insn
|
||||
* is consitional
|
||||
*/
|
||||
insn = (__mem_to_opcode_arm(
|
||||
op->optinsn.copied_insn[0]) & 0xf0000000) |
|
||||
(insn & 0x0fffffff);
|
||||
|
||||
/*
|
||||
* Similar to __arch_disarm_kprobe, operations which
|
||||
* removing breakpoints must be wrapped by stop_machine
|
||||
* to avoid racing.
|
||||
*/
|
||||
kprobes_remove_breakpoint(op->kp.addr, insn);
|
||||
|
||||
list_del_init(&op->list);
|
||||
}
|
||||
}
|
||||
|
||||
void arch_unoptimize_kprobe(struct optimized_kprobe *op)
|
||||
{
|
||||
arch_arm_kprobe(&op->kp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recover original instructions and breakpoints from relative jumps.
|
||||
* Caller must call with locking kprobe_mutex.
|
||||
*/
|
||||
void arch_unoptimize_kprobes(struct list_head *oplist,
|
||||
struct list_head *done_list)
|
||||
{
|
||||
struct optimized_kprobe *op, *tmp;
|
||||
|
||||
list_for_each_entry_safe(op, tmp, oplist, list) {
|
||||
arch_unoptimize_kprobe(op);
|
||||
list_move(&op->list, done_list);
|
||||
}
|
||||
}
|
||||
|
||||
int arch_within_optimized_kprobe(struct optimized_kprobe *op,
|
||||
unsigned long addr)
|
||||
{
|
||||
return ((unsigned long)op->kp.addr <= addr &&
|
||||
(unsigned long)op->kp.addr + RELATIVEJUMP_SIZE > addr);
|
||||
}
|
||||
|
||||
void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
|
||||
{
|
||||
__arch_remove_optimized_kprobe(op, 1);
|
||||
}
|
@ -12,8 +12,9 @@
|
||||
#include <linux/module.h>
|
||||
#include <asm/system_info.h>
|
||||
#include <asm/opcodes.h>
|
||||
#include <asm/probes.h>
|
||||
|
||||
#include "kprobes-test.h"
|
||||
#include "test-core.h"
|
||||
|
||||
|
||||
#define TEST_ISA "32"
|
||||
@ -203,9 +204,9 @@ void kprobe_arm_test_cases(void)
|
||||
#endif
|
||||
TEST_GROUP("Miscellaneous instructions")
|
||||
|
||||
TEST("mrs r0, cpsr")
|
||||
TEST("mrspl r7, cpsr")
|
||||
TEST("mrs r14, cpsr")
|
||||
TEST_RMASKED("mrs r",0,~PSR_IGNORE_BITS,", cpsr")
|
||||
TEST_RMASKED("mrspl r",7,~PSR_IGNORE_BITS,", cpsr")
|
||||
TEST_RMASKED("mrs r",14,~PSR_IGNORE_BITS,", cpsr")
|
||||
TEST_UNSUPPORTED(__inst_arm(0xe10ff000) " @ mrs r15, cpsr")
|
||||
TEST_UNSUPPORTED("mrs r0, spsr")
|
||||
TEST_UNSUPPORTED("mrs lr, spsr")
|
||||
@ -214,9 +215,12 @@ void kprobe_arm_test_cases(void)
|
||||
TEST_UNSUPPORTED("msr cpsr_f, lr")
|
||||
TEST_UNSUPPORTED("msr spsr, r0")
|
||||
|
||||
#if __LINUX_ARM_ARCH__ >= 5 || \
|
||||
(__LINUX_ARM_ARCH__ == 4 && !defined(CONFIG_CPU_32v4))
|
||||
TEST_BF_R("bx r",0,2f,"")
|
||||
TEST_BB_R("bx r",7,2f,"")
|
||||
TEST_BF_R("bxeq r",14,2f,"")
|
||||
#endif
|
||||
|
||||
#if __LINUX_ARM_ARCH__ >= 5
|
||||
TEST_R("clz r0, r",0, 0x0,"")
|
||||
@ -476,7 +480,9 @@ void kprobe_arm_test_cases(void)
|
||||
TEST_GROUP("Extra load/store instructions")
|
||||
|
||||
TEST_RPR( "strh r",0, VAL1,", [r",1, 48,", -r",2, 24,"]")
|
||||
TEST_RPR( "streqh r",14,VAL2,", [r",13,0, ", r",12, 48,"]")
|
||||
TEST_RPR( "streqh r",14,VAL2,", [r",11,0, ", r",12, 48,"]")
|
||||
TEST_UNSUPPORTED( "streqh r14, [r13, r12]")
|
||||
TEST_UNSUPPORTED( "streqh r14, [r12, r13]")
|
||||
TEST_RPR( "strh r",1, VAL1,", [r",2, 24,", r",3, 48,"]!")
|
||||
TEST_RPR( "strneh r",12,VAL2,", [r",11,48,", -r",10,24,"]!")
|
||||
TEST_RPR( "strh r",2, VAL1,", [r",3, 24,"], r",4, 48,"")
|
||||
@ -501,6 +507,9 @@ void kprobe_arm_test_cases(void)
|
||||
TEST_RP( "strplh r",12,VAL2,", [r",11,24,", #-4]!")
|
||||
TEST_RP( "strh r",2, VAL1,", [r",3, 24,"], #48")
|
||||
TEST_RP( "strh r",10,VAL2,", [r",9, 64,"], #-48")
|
||||
TEST_RP( "strh r",3, VAL1,", [r",13,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!")
|
||||
TEST_UNSUPPORTED("strh r3, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!")
|
||||
TEST_RP( "strh r",4, VAL1,", [r",14,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!")
|
||||
TEST_UNSUPPORTED(__inst_arm(0xe1efc3b0) " @ strh r12, [pc, #48]!")
|
||||
TEST_UNSUPPORTED(__inst_arm(0xe0c9f3b0) " @ strh pc, [r9], #48")
|
||||
|
||||
@ -565,7 +574,9 @@ void kprobe_arm_test_cases(void)
|
||||
|
||||
#if __LINUX_ARM_ARCH__ >= 5
|
||||
TEST_RPR( "strd r",0, VAL1,", [r",1, 48,", -r",2,24,"]")
|
||||
TEST_RPR( "strccd r",8, VAL2,", [r",13,0, ", r",12,48,"]")
|
||||
TEST_RPR( "strccd r",8, VAL2,", [r",11,0, ", r",12,48,"]")
|
||||
TEST_UNSUPPORTED( "strccd r8, [r13, r12]")
|
||||
TEST_UNSUPPORTED( "strccd r8, [r12, r13]")
|
||||
TEST_RPR( "strd r",4, VAL1,", [r",2, 24,", r",3, 48,"]!")
|
||||
TEST_RPR( "strcsd r",12,VAL2,", [r",11,48,", -r",10,24,"]!")
|
||||
TEST_RPR( "strd r",2, VAL1,", [r",5, 24,"], r",4,48,"")
|
||||
@ -589,6 +600,9 @@ void kprobe_arm_test_cases(void)
|
||||
TEST_RP( "strvcd r",12,VAL2,", [r",11,24,", #-16]!")
|
||||
TEST_RP( "strd r",2, VAL1,", [r",4, 24,"], #48")
|
||||
TEST_RP( "strd r",10,VAL2,", [r",9, 64,"], #-48")
|
||||
TEST_RP( "strd r",6, VAL1,", [r",13,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!")
|
||||
TEST_UNSUPPORTED("strd r6, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!")
|
||||
TEST_RP( "strd r",4, VAL1,", [r",12,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!")
|
||||
TEST_UNSUPPORTED(__inst_arm(0xe1efc3f0) " @ strd r12, [pc, #48]!")
|
||||
|
||||
TEST_P( "ldrd r0, [r",0, 24,", #-8]")
|
||||
@ -637,14 +651,20 @@ void kprobe_arm_test_cases(void)
|
||||
TEST_RP( "str"byte" r",12,VAL2,", [r",11,24,", #-4]!") \
|
||||
TEST_RP( "str"byte" r",2, VAL1,", [r",3, 24,"], #48") \
|
||||
TEST_RP( "str"byte" r",10,VAL2,", [r",9, 64,"], #-48") \
|
||||
TEST_RP( "str"byte" r",3, VAL1,", [r",13,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!") \
|
||||
TEST_UNSUPPORTED("str"byte" r3, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!") \
|
||||
TEST_RP( "str"byte" r",4, VAL1,", [r",10,TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!") \
|
||||
TEST_RPR("str"byte" r",0, VAL1,", [r",1, 48,", -r",2, 24,"]") \
|
||||
TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 48,"]") \
|
||||
TEST_RPR("str"byte" r",14,VAL2,", [r",11,0, ", r",12, 48,"]") \
|
||||
TEST_UNSUPPORTED("str"byte" r14, [r13, r12]") \
|
||||
TEST_UNSUPPORTED("str"byte" r14, [r12, r13]") \
|
||||
TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 48,"]!") \
|
||||
TEST_RPR("str"byte" r",12,VAL2,", [r",11,48,", -r",10,24,"]!") \
|
||||
TEST_RPR("str"byte" r",2, VAL1,", [r",3, 24,"], r",4, 48,"") \
|
||||
TEST_RPR("str"byte" r",10,VAL2,", [r",9, 48,"], -r",11,24,"") \
|
||||
TEST_RPR("str"byte" r",0, VAL1,", [r",1, 24,", r",2, 32,", asl #1]")\
|
||||
TEST_RPR("str"byte" r",14,VAL2,", [r",13,0, ", r",12, 32,", lsr #2]")\
|
||||
TEST_RPR("str"byte" r",14,VAL2,", [r",11,0, ", r",12, 32,", lsr #2]")\
|
||||
TEST_UNSUPPORTED("str"byte" r14, [r13, r12, lsr #2]") \
|
||||
TEST_RPR("str"byte" r",1, VAL1,", [r",2, 24,", r",3, 32,", asr #3]!")\
|
||||
TEST_RPR("str"byte" r",12,VAL2,", [r",11,24,", r",10, 4,", ror #31]!")\
|
||||
TEST_P( "ldr"byte" r0, [r",0, 24,", #-2]") \
|
||||
@ -668,12 +688,12 @@ void kprobe_arm_test_cases(void)
|
||||
|
||||
LOAD_STORE("")
|
||||
TEST_P( "str pc, [r",0,0,", #15*4]")
|
||||
TEST_R( "str pc, [sp, r",2,15*4,"]")
|
||||
TEST_UNSUPPORTED( "str pc, [sp, r2]")
|
||||
TEST_BF( "ldr pc, [sp, #15*4]")
|
||||
TEST_BF_R("ldr pc, [sp, r",2,15*4,"]")
|
||||
|
||||
TEST_P( "str sp, [r",0,0,", #13*4]")
|
||||
TEST_R( "str sp, [sp, r",2,13*4,"]")
|
||||
TEST_UNSUPPORTED( "str sp, [sp, r2]")
|
||||
TEST_BF( "ldr sp, [sp, #13*4]")
|
||||
TEST_BF_R("ldr sp, [sp, r",2,13*4,"]")
|
||||
|
@ -209,10 +209,10 @@
|
||||
#include <linux/bug.h>
|
||||
#include <asm/opcodes.h>
|
||||
|
||||
#include "kprobes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "probes-thumb.h"
|
||||
#include "kprobes-test.h"
|
||||
#include "core.h"
|
||||
#include "test-core.h"
|
||||
#include "../decode-arm.h"
|
||||
#include "../decode-thumb.h"
|
||||
|
||||
|
||||
#define BENCHMARKING 1
|
||||
@ -236,6 +236,8 @@ static int tests_failed;
|
||||
|
||||
#ifndef CONFIG_THUMB2_KERNEL
|
||||
|
||||
#define RET(reg) "mov pc, "#reg
|
||||
|
||||
long arm_func(long r0, long r1);
|
||||
|
||||
static void __used __naked __arm_kprobes_test_func(void)
|
||||
@ -245,7 +247,7 @@ static void __used __naked __arm_kprobes_test_func(void)
|
||||
".type arm_func, %%function \n\t"
|
||||
"arm_func: \n\t"
|
||||
"adds r0, r0, r1 \n\t"
|
||||
"bx lr \n\t"
|
||||
"mov pc, lr \n\t"
|
||||
".code "NORMAL_ISA /* Back to Thumb if necessary */
|
||||
: : : "r0", "r1", "cc"
|
||||
);
|
||||
@ -253,6 +255,8 @@ static void __used __naked __arm_kprobes_test_func(void)
|
||||
|
||||
#else /* CONFIG_THUMB2_KERNEL */
|
||||
|
||||
#define RET(reg) "bx "#reg
|
||||
|
||||
long thumb16_func(long r0, long r1);
|
||||
long thumb32even_func(long r0, long r1);
|
||||
long thumb32odd_func(long r0, long r1);
|
||||
@ -494,7 +498,7 @@ static void __naked benchmark_nop(void)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
"nop \n\t"
|
||||
"bx lr"
|
||||
RET(lr)" \n\t"
|
||||
);
|
||||
}
|
||||
|
||||
@ -977,7 +981,7 @@ void __naked __kprobes_test_case_start(void)
|
||||
"bic r0, lr, #1 @ r0 = inline data \n\t"
|
||||
"mov r1, sp \n\t"
|
||||
"bl kprobes_test_case_start \n\t"
|
||||
"bx r0 \n\t"
|
||||
RET(r0)" \n\t"
|
||||
);
|
||||
}
|
||||
|
||||
@ -1056,15 +1060,6 @@ static int test_case_run_count;
|
||||
static bool test_case_is_thumb;
|
||||
static int test_instance;
|
||||
|
||||
/*
|
||||
* We ignore the state of the imprecise abort disable flag (CPSR.A) because this
|
||||
* can change randomly as the kernel doesn't take care to preserve or initialise
|
||||
* this across context switches. Also, with Security Extentions, the flag may
|
||||
* not be under control of the kernel; for this reason we ignore the state of
|
||||
* the FIQ disable flag CPSR.F as well.
|
||||
*/
|
||||
#define PSR_IGNORE_BITS (PSR_A_BIT | PSR_F_BIT)
|
||||
|
||||
static unsigned long test_check_cc(int cc, unsigned long cpsr)
|
||||
{
|
||||
int ret = arm_check_condition(cc << 28, cpsr);
|
||||
@ -1196,6 +1191,13 @@ static void setup_test_context(struct pt_regs *regs)
|
||||
regs->uregs[arg->reg] =
|
||||
(unsigned long)current_stack + arg->val;
|
||||
memory_needs_checking = true;
|
||||
/*
|
||||
* Test memory at an address below SP is in danger of
|
||||
* being altered by an interrupt occurring and pushing
|
||||
* data onto the stack. Disable interrupts to stop this.
|
||||
*/
|
||||
if (arg->reg == 13)
|
||||
regs->ARM_cpsr |= PSR_I_BIT;
|
||||
break;
|
||||
}
|
||||
case ARG_TYPE_MEM: {
|
||||
@ -1264,14 +1266,26 @@ test_case_pre_handler(struct kprobe *p, struct pt_regs *regs)
|
||||
static int __kprobes
|
||||
test_after_pre_handler(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct test_arg *args;
|
||||
|
||||
if (container_of(p, struct test_probe, kprobe)->hit == test_instance)
|
||||
return 0; /* Already run for this test instance */
|
||||
|
||||
result_regs = *regs;
|
||||
|
||||
/* Mask out results which are indeterminate */
|
||||
result_regs.ARM_cpsr &= ~PSR_IGNORE_BITS;
|
||||
for (args = current_args; args[0].type != ARG_TYPE_END; ++args)
|
||||
if (args[0].type == ARG_TYPE_REG_MASKED) {
|
||||
struct test_arg_regptr *arg =
|
||||
(struct test_arg_regptr *)args;
|
||||
result_regs.uregs[arg->reg] &= arg->val;
|
||||
}
|
||||
|
||||
/* Undo any changes done to SP by the test case */
|
||||
regs->ARM_sp = (unsigned long)current_stack;
|
||||
/* Enable interrupts in case setup_test_context disabled them */
|
||||
regs->ARM_cpsr &= ~PSR_I_BIT;
|
||||
|
||||
container_of(p, struct test_probe, kprobe)->hit = test_instance;
|
||||
return 0;
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes-test.h
|
||||
* arch/arm/probes/kprobes/test-core.h
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
@ -45,10 +45,11 @@ extern int kprobe_test_cc_position;
|
||||
*
|
||||
*/
|
||||
|
||||
#define ARG_TYPE_END 0
|
||||
#define ARG_TYPE_REG 1
|
||||
#define ARG_TYPE_PTR 2
|
||||
#define ARG_TYPE_MEM 3
|
||||
#define ARG_TYPE_END 0
|
||||
#define ARG_TYPE_REG 1
|
||||
#define ARG_TYPE_PTR 2
|
||||
#define ARG_TYPE_MEM 3
|
||||
#define ARG_TYPE_REG_MASKED 4
|
||||
|
||||
#define ARG_FLAG_UNSUPPORTED 0x01
|
||||
#define ARG_FLAG_SUPPORTED 0x02
|
||||
@ -61,7 +62,7 @@ struct test_arg {
|
||||
};
|
||||
|
||||
struct test_arg_regptr {
|
||||
u8 type; /* ARG_TYPE_REG or ARG_TYPE_PTR */
|
||||
u8 type; /* ARG_TYPE_REG or ARG_TYPE_PTR or ARG_TYPE_REG_MASKED */
|
||||
u8 reg;
|
||||
u8 _padding[2];
|
||||
u32 val;
|
||||
@ -138,6 +139,12 @@ struct test_arg_end {
|
||||
".short 0 \n\t" \
|
||||
".word "#val" \n\t"
|
||||
|
||||
#define TEST_ARG_REG_MASKED(reg, val) \
|
||||
".byte "__stringify(ARG_TYPE_REG_MASKED)" \n\t" \
|
||||
".byte "#reg" \n\t" \
|
||||
".short 0 \n\t" \
|
||||
".word "#val" \n\t"
|
||||
|
||||
#define TEST_ARG_END(flags) \
|
||||
".byte "__stringify(ARG_TYPE_END)" \n\t" \
|
||||
".byte "TEST_ISA flags" \n\t" \
|
||||
@ -395,6 +402,22 @@ struct test_arg_end {
|
||||
" "codex" \n\t" \
|
||||
TESTCASE_END
|
||||
|
||||
#define TEST_RMASKED(code1, reg, mask, code2) \
|
||||
TESTCASE_START(code1 #reg code2) \
|
||||
TEST_ARG_REG_MASKED(reg, mask) \
|
||||
TEST_ARG_END("") \
|
||||
TEST_INSTRUCTION(code1 #reg code2) \
|
||||
TESTCASE_END
|
||||
|
||||
/*
|
||||
* We ignore the state of the imprecise abort disable flag (CPSR.A) because this
|
||||
* can change randomly as the kernel doesn't take care to preserve or initialise
|
||||
* this across context switches. Also, with Security Extensions, the flag may
|
||||
* not be under control of the kernel; for this reason we ignore the state of
|
||||
* the FIQ disable flag CPSR.F as well.
|
||||
*/
|
||||
#define PSR_IGNORE_BITS (PSR_A_BIT | PSR_F_BIT)
|
||||
|
||||
|
||||
/*
|
||||
* Macros for defining space directives spread over multiple lines.
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/kernel/kprobes-test-thumb.c
|
||||
* arch/arm/probes/kprobes/test-thumb.c
|
||||
*
|
||||
* Copyright (C) 2011 Jon Medhurst <tixy@yxit.co.uk>.
|
||||
*
|
||||
@ -11,8 +11,9 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/opcodes.h>
|
||||
#include <asm/probes.h>
|
||||
|
||||
#include "kprobes-test.h"
|
||||
#include "test-core.h"
|
||||
|
||||
|
||||
#define TEST_ISA "16"
|
||||
@ -416,6 +417,9 @@ void kprobe_thumb32_test_cases(void)
|
||||
TEST_RR( "strd r",14,VAL2,", r",12,VAL1,", [sp, #16]!")
|
||||
TEST_RRP("strd r",1, VAL1,", r",0, VAL2,", [r",7, 24,"], #16")
|
||||
TEST_RR( "strd r",7, VAL2,", r",8, VAL1,", [sp], #-16")
|
||||
TEST_RRP("strd r",6, VAL1,", r",7, VAL2,", [r",13, TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!")
|
||||
TEST_UNSUPPORTED("strd r6, r7, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!")
|
||||
TEST_RRP("strd r",4, VAL1,", r",5, VAL2,", [r",14, TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!")
|
||||
TEST_UNSUPPORTED(__inst_thumb32(0xe9efec04) " @ strd r14, r12, [pc, #16]!")
|
||||
TEST_UNSUPPORTED(__inst_thumb32(0xe8efec04) " @ strd r14, r12, [pc], #16")
|
||||
|
||||
@ -774,8 +778,8 @@ CONDITION_INSTRUCTIONS(22,
|
||||
|
||||
TEST_UNSUPPORTED("subs pc, lr, #4")
|
||||
|
||||
TEST("mrs r0, cpsr")
|
||||
TEST("mrs r14, cpsr")
|
||||
TEST_RMASKED("mrs r",0,~PSR_IGNORE_BITS,", cpsr")
|
||||
TEST_RMASKED("mrs r",14,~PSR_IGNORE_BITS,", cpsr")
|
||||
TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8d00) " @ mrs sp, spsr")
|
||||
TEST_UNSUPPORTED(__inst_thumb32(0xf3ef8f00) " @ mrs pc, spsr")
|
||||
TEST_UNSUPPORTED("mrs r0, spsr")
|
||||
@ -821,14 +825,22 @@ CONDITION_INSTRUCTIONS(22,
|
||||
TEST_RP( "str"size" r",14,VAL2,", [r",1, 256, ", #-128]!") \
|
||||
TEST_RPR("str"size".w r",0, VAL1,", [r",1, 0,", r",2, 4,"]") \
|
||||
TEST_RPR("str"size" r",14,VAL2,", [r",10,0,", r",11,4,", lsl #1]") \
|
||||
TEST_UNSUPPORTED("str"size" r0, [r13, r1]") \
|
||||
TEST_R( "str"size".w r",7, VAL1,", [sp, #24]") \
|
||||
TEST_RP( "str"size".w r",0, VAL2,", [r",0,0, "]") \
|
||||
TEST_RP( "str"size" r",6, VAL1,", [r",13, TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"]!") \
|
||||
TEST_UNSUPPORTED("str"size" r6, [r13, #-"__stringify(MAX_STACK_SIZE)"-8]!") \
|
||||
TEST_RP( "str"size" r",4, VAL2,", [r",12, TEST_MEMORY_SIZE,", #-"__stringify(MAX_STACK_SIZE)"-8]!") \
|
||||
TEST_UNSUPPORTED("str"size"t r0, [r1, #4]")
|
||||
|
||||
SINGLE_STORE("b")
|
||||
SINGLE_STORE("h")
|
||||
SINGLE_STORE("")
|
||||
|
||||
TEST_UNSUPPORTED(__inst_thumb32(0xf801000d) " @ strb r0, [r1, r13]")
|
||||
TEST_UNSUPPORTED(__inst_thumb32(0xf821000d) " @ strh r0, [r1, r13]")
|
||||
TEST_UNSUPPORTED(__inst_thumb32(0xf841000d) " @ str r0, [r1, r13]")
|
||||
|
||||
TEST("str sp, [sp]")
|
||||
TEST_UNSUPPORTED(__inst_thumb32(0xf8cfe000) " @ str r14, [pc]")
|
||||
TEST_UNSUPPORTED(__inst_thumb32(0xf8cef000) " @ str pc, [r14]")
|
1
arch/arm/probes/uprobes/Makefile
Normal file
1
arch/arm/probes/uprobes/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-$(CONFIG_UPROBES) += core.o actions-arm.o
|
@ -13,9 +13,9 @@
|
||||
#include <linux/uprobes.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "uprobes.h"
|
||||
#include "../decode.h"
|
||||
#include "../decode-arm.h"
|
||||
#include "core.h"
|
||||
|
||||
static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs)
|
||||
{
|
||||
@ -195,8 +195,6 @@ uprobe_decode_ldmstm(probes_opcode_t insn,
|
||||
}
|
||||
|
||||
const union decode_action uprobes_probes_actions[] = {
|
||||
[PROBES_EMULATE_NONE] = {.handler = probes_simulate_nop},
|
||||
[PROBES_SIMULATE_NOP] = {.handler = probes_simulate_nop},
|
||||
[PROBES_PRELOAD_IMM] = {.handler = probes_simulate_nop},
|
||||
[PROBES_PRELOAD_REG] = {.handler = probes_simulate_nop},
|
||||
[PROBES_BRANCH_IMM] = {.handler = simulate_blx1},
|
@ -17,9 +17,9 @@
|
||||
#include <asm/opcodes.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "uprobes.h"
|
||||
#include "../decode.h"
|
||||
#include "../decode-arm.h"
|
||||
#include "core.h"
|
||||
|
||||
#define UPROBE_TRAP_NR UINT_MAX
|
||||
|
||||
@ -88,7 +88,7 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
|
||||
auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);
|
||||
|
||||
ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
|
||||
uprobes_probes_actions);
|
||||
uprobes_probes_actions, NULL);
|
||||
switch (ret) {
|
||||
case INSN_REJECTED:
|
||||
return -EINVAL;
|
@ -1,6 +1,7 @@
|
||||
# CONFIG_LOCALVERSION_AUTO is not set
|
||||
CONFIG_SYSVIPC=y
|
||||
CONFIG_POSIX_MQUEUE=y
|
||||
CONFIG_FHANDLE=y
|
||||
CONFIG_AUDIT=y
|
||||
CONFIG_NO_HZ_IDLE=y
|
||||
CONFIG_HIGH_RES_TIMERS=y
|
||||
@ -13,14 +14,12 @@ CONFIG_TASK_IO_ACCOUNTING=y
|
||||
CONFIG_IKCONFIG=y
|
||||
CONFIG_IKCONFIG_PROC=y
|
||||
CONFIG_LOG_BUF_SHIFT=14
|
||||
CONFIG_RESOURCE_COUNTERS=y
|
||||
CONFIG_MEMCG=y
|
||||
CONFIG_MEMCG_SWAP=y
|
||||
CONFIG_MEMCG_KMEM=y
|
||||
CONFIG_CGROUP_HUGETLB=y
|
||||
# CONFIG_UTS_NS is not set
|
||||
# CONFIG_IPC_NS is not set
|
||||
# CONFIG_PID_NS is not set
|
||||
# CONFIG_NET_NS is not set
|
||||
CONFIG_SCHED_AUTOGROUP=y
|
||||
CONFIG_BLK_DEV_INITRD=y
|
||||
@ -92,7 +91,6 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
|
||||
CONFIG_SERIAL_OF_PLATFORM=y
|
||||
CONFIG_VIRTIO_CONSOLE=y
|
||||
# CONFIG_HW_RANDOM is not set
|
||||
# CONFIG_HMC_DRV is not set
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_PL022=y
|
||||
CONFIG_GPIO_PL061=y
|
||||
@ -133,6 +131,8 @@ CONFIG_EXT3_FS=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_FANOTIFY=y
|
||||
CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
|
||||
CONFIG_QUOTA=y
|
||||
CONFIG_AUTOFS4_FS=y
|
||||
CONFIG_FUSE_FS=y
|
||||
CONFIG_CUSE=y
|
||||
CONFIG_VFAT_FS=y
|
||||
@ -152,14 +152,15 @@ CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_DEBUG_KERNEL=y
|
||||
CONFIG_LOCKUP_DETECTOR=y
|
||||
# CONFIG_SCHED_DEBUG is not set
|
||||
# CONFIG_DEBUG_PREEMPT is not set
|
||||
# CONFIG_FTRACE is not set
|
||||
CONFIG_KEYS=y
|
||||
CONFIG_SECURITY=y
|
||||
CONFIG_CRYPTO_ANSI_CPRNG=y
|
||||
CONFIG_ARM64_CRYPTO=y
|
||||
CONFIG_CRYPTO_SHA1_ARM64_CE=y
|
||||
CONFIG_CRYPTO_SHA2_ARM64_CE=y
|
||||
CONFIG_CRYPTO_GHASH_ARM64_CE=y
|
||||
CONFIG_CRYPTO_AES_ARM64_CE=y
|
||||
CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
|
||||
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
|
||||
CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
|
||||
|
@ -52,13 +52,14 @@ static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops)
|
||||
dev->archdata.dma_ops = ops;
|
||||
}
|
||||
|
||||
static inline int set_arch_dma_coherent_ops(struct device *dev)
|
||||
static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
|
||||
struct iommu_ops *iommu, bool coherent)
|
||||
{
|
||||
dev->archdata.dma_coherent = true;
|
||||
set_dma_ops(dev, &coherent_swiotlb_dma_ops);
|
||||
return 0;
|
||||
dev->archdata.dma_coherent = coherent;
|
||||
if (coherent)
|
||||
set_dma_ops(dev, &coherent_swiotlb_dma_ops);
|
||||
}
|
||||
#define set_arch_dma_coherent_ops set_arch_dma_coherent_ops
|
||||
#define arch_setup_dma_ops arch_setup_dma_ops
|
||||
|
||||
/* do not use this function in a driver */
|
||||
static inline bool is_device_dma_coherent(struct device *dev)
|
||||
|
@ -298,7 +298,6 @@ void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address,
|
||||
#define pfn_pmd(pfn,prot) (__pmd(((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot)))
|
||||
#define mk_pmd(page,prot) pfn_pmd(page_to_pfn(page),prot)
|
||||
|
||||
#define pmd_page(pmd) pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK))
|
||||
#define pud_write(pud) pte_write(pud_pte(pud))
|
||||
#define pud_pfn(pud) (((pud_val(pud) & PUD_MASK) & PHYS_MASK) >> PAGE_SHIFT)
|
||||
|
||||
@ -401,7 +400,7 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
|
||||
return (pmd_t *)pud_page_vaddr(*pud) + pmd_index(addr);
|
||||
}
|
||||
|
||||
#define pud_page(pud) pmd_page(pud_pmd(pud))
|
||||
#define pud_page(pud) pfn_to_page(__phys_to_pfn(pud_val(pud) & PHYS_MASK))
|
||||
|
||||
#endif /* CONFIG_ARM64_PGTABLE_LEVELS > 2 */
|
||||
|
||||
@ -437,6 +436,8 @@ static inline pud_t *pud_offset(pgd_t *pgd, unsigned long addr)
|
||||
return (pud_t *)pgd_page_vaddr(*pgd) + pud_index(addr);
|
||||
}
|
||||
|
||||
#define pgd_page(pgd) pfn_to_page(__phys_to_pfn(pgd_val(pgd) & PHYS_MASK))
|
||||
|
||||
#endif /* CONFIG_ARM64_PGTABLE_LEVELS > 3 */
|
||||
|
||||
#define pgd_ERROR(pgd) __pgd_error(__FILE__, __LINE__, pgd_val(pgd))
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <asm/debug-monitors.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/memory.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <asm/tlbflush.h>
|
||||
@ -98,7 +99,18 @@ int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
|
||||
*/
|
||||
ret = __cpu_suspend_enter(arg, fn);
|
||||
if (ret == 0) {
|
||||
cpu_switch_mm(mm->pgd, mm);
|
||||
/*
|
||||
* We are resuming from reset with TTBR0_EL1 set to the
|
||||
* idmap to enable the MMU; restore the active_mm mappings in
|
||||
* TTBR0_EL1 unless the active_mm == &init_mm, in which case
|
||||
* the thread entered __cpu_suspend with TTBR0_EL1 set to
|
||||
* reserved TTBR0 page tables and should be restored as such.
|
||||
*/
|
||||
if (mm == &init_mm)
|
||||
cpu_set_reserved_ttbr0();
|
||||
else
|
||||
cpu_switch_mm(mm->pgd, mm);
|
||||
|
||||
flush_tlb_all();
|
||||
|
||||
/*
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
|
||||
|
||||
#define NR_syscalls 318 /* length of syscall table */
|
||||
#define NR_syscalls 319 /* length of syscall table */
|
||||
|
||||
/*
|
||||
* The following defines stop scripts/checksyscalls.sh from complaining about
|
||||
|
@ -331,5 +331,6 @@
|
||||
#define __NR_getrandom 1339
|
||||
#define __NR_memfd_create 1340
|
||||
#define __NR_bpf 1341
|
||||
#define __NR_execveat 1342
|
||||
|
||||
#endif /* _UAPI_ASM_IA64_UNISTD_H */
|
||||
|
@ -1779,6 +1779,7 @@ sys_call_table:
|
||||
data8 sys_getrandom
|
||||
data8 sys_memfd_create // 1340
|
||||
data8 sys_bpf
|
||||
data8 sys_execveat
|
||||
|
||||
.org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls
|
||||
#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
|
||||
|
@ -72,6 +72,7 @@ void __init setup_cpuinfo(void)
|
||||
cpuinfo.has_div = fcpu_has(cpu, "altr,has-div");
|
||||
cpuinfo.has_mul = fcpu_has(cpu, "altr,has-mul");
|
||||
cpuinfo.has_mulx = fcpu_has(cpu, "altr,has-mulx");
|
||||
cpuinfo.mmu = fcpu_has(cpu, "altr,has-mmu");
|
||||
|
||||
if (IS_ENABLED(CONFIG_NIOS2_HW_DIV_SUPPORT) && !cpuinfo.has_div)
|
||||
err_cpu("DIV");
|
||||
|
@ -365,30 +365,14 @@ ENTRY(ret_from_interrupt)
|
||||
GET_THREAD_INFO r1
|
||||
ldw r4, TI_PREEMPT_COUNT(r1)
|
||||
bne r4, r0, restore_all
|
||||
|
||||
need_resched:
|
||||
ldw r4, TI_FLAGS(r1) /* ? Need resched set */
|
||||
BTBZ r10, r4, TIF_NEED_RESCHED, restore_all
|
||||
ldw r4, PT_ESTATUS(sp) /* ? Interrupts off */
|
||||
andi r10, r4, ESTATUS_EPIE
|
||||
beq r10, r0, restore_all
|
||||
movia r4, PREEMPT_ACTIVE
|
||||
stw r4, TI_PREEMPT_COUNT(r1)
|
||||
rdctl r10, status /* enable intrs again */
|
||||
ori r10, r10 ,STATUS_PIE
|
||||
wrctl status, r10
|
||||
PUSH r1
|
||||
call schedule
|
||||
POP r1
|
||||
mov r4, r0
|
||||
stw r4, TI_PREEMPT_COUNT(r1)
|
||||
rdctl r10, status /* disable intrs */
|
||||
andi r10, r10, %lo(~STATUS_PIE)
|
||||
wrctl status, r10
|
||||
br need_resched
|
||||
#else
|
||||
br restore_all
|
||||
call preempt_schedule_irq
|
||||
#endif
|
||||
br restore_all
|
||||
|
||||
/***********************************************************************
|
||||
* A few syscall wrappers
|
||||
|
@ -33,11 +33,18 @@
|
||||
|
||||
#endif /*!CONFIG_PA20*/
|
||||
|
||||
/* LDCW, the only atomic read-write operation PA-RISC has. *sigh*. */
|
||||
/* LDCW, the only atomic read-write operation PA-RISC has. *sigh*.
|
||||
We don't explicitly expose that "*a" may be written as reload
|
||||
fails to find a register in class R1_REGS when "a" needs to be
|
||||
reloaded when generating 64-bit PIC code. Instead, we clobber
|
||||
memory to indicate to the compiler that the assembly code reads
|
||||
or writes to items other than those listed in the input and output
|
||||
operands. This may pessimize the code somewhat but __ldcw is
|
||||
usually used within code blocks surrounded by memory barriors. */
|
||||
#define __ldcw(a) ({ \
|
||||
unsigned __ret; \
|
||||
__asm__ __volatile__(__LDCW " 0(%2),%0" \
|
||||
: "=r" (__ret), "+m" (*(a)) : "r" (a)); \
|
||||
__asm__ __volatile__(__LDCW " 0(%1),%0" \
|
||||
: "=r" (__ret) : "r" (a) : "memory"); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
|
@ -86,6 +86,11 @@ extern int overlaps_crashkernel(unsigned long start, unsigned long size);
|
||||
extern void reserve_crashkernel(void);
|
||||
extern void machine_kexec_mask_interrupts(void);
|
||||
|
||||
static inline bool kdump_in_progress(void)
|
||||
{
|
||||
return crashing_cpu >= 0;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_KEXEC */
|
||||
static inline void crash_kexec_secondary(struct pt_regs *regs) { }
|
||||
|
||||
@ -106,6 +111,11 @@ static inline int crash_shutdown_unregister(crash_shutdown_t handler)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool kdump_in_progress(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KEXEC */
|
||||
#endif /* ! __ASSEMBLY__ */
|
||||
#endif /* __KERNEL__ */
|
||||
|
@ -366,3 +366,4 @@ SYSCALL_SPU(seccomp)
|
||||
SYSCALL_SPU(getrandom)
|
||||
SYSCALL_SPU(memfd_create)
|
||||
SYSCALL_SPU(bpf)
|
||||
COMPAT_SYS(execveat)
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <uapi/asm/unistd.h>
|
||||
|
||||
|
||||
#define __NR_syscalls 362
|
||||
#define __NR_syscalls 363
|
||||
|
||||
#define __NR__exit __NR_exit
|
||||
#define NR_syscalls __NR_syscalls
|
||||
|
@ -384,5 +384,6 @@
|
||||
#define __NR_getrandom 359
|
||||
#define __NR_memfd_create 360
|
||||
#define __NR_bpf 361
|
||||
#define __NR_execveat 362
|
||||
|
||||
#endif /* _UAPI_ASM_POWERPC_UNISTD_H_ */
|
||||
|
@ -330,7 +330,7 @@ void default_machine_kexec(struct kimage *image)
|
||||
* using debugger IPI.
|
||||
*/
|
||||
|
||||
if (crashing_cpu == -1)
|
||||
if (!kdump_in_progress())
|
||||
kexec_prepare_cpus();
|
||||
|
||||
pr_debug("kexec: Starting switchover sequence.\n");
|
||||
|
@ -700,6 +700,7 @@ void start_secondary(void *unused)
|
||||
smp_store_cpu_info(cpu);
|
||||
set_dec(tb_ticks_per_jiffy);
|
||||
preempt_disable();
|
||||
cpu_callin_map[cpu] = 1;
|
||||
|
||||
if (smp_ops->setup_cpu)
|
||||
smp_ops->setup_cpu(cpu);
|
||||
@ -738,14 +739,6 @@ void start_secondary(void *unused)
|
||||
notify_cpu_starting(cpu);
|
||||
set_cpu_online(cpu, true);
|
||||
|
||||
/*
|
||||
* CPU must be marked active and online before we signal back to the
|
||||
* master, because the scheduler needs to see the cpu_online and
|
||||
* cpu_active bits set.
|
||||
*/
|
||||
smp_wmb();
|
||||
cpu_callin_map[cpu] = 1;
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
cpu_startup_entry(CPUHP_ONLINE);
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include <asm/trace.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/plpar_wrappers.h>
|
||||
#include <asm/kexec.h>
|
||||
#include <asm/fadump.h>
|
||||
|
||||
#include "pseries.h"
|
||||
@ -267,8 +268,13 @@ static void pSeries_lpar_hptab_clear(void)
|
||||
* out to the user, but at least this will stop us from
|
||||
* continuing on further and creating an even more
|
||||
* difficult to debug situation.
|
||||
*
|
||||
* There is a known problem when kdump'ing, if cpus are offline
|
||||
* the above call will fail. Rather than panicking again, keep
|
||||
* going and hope the kdump kernel is also little endian, which
|
||||
* it usually is.
|
||||
*/
|
||||
if (rc)
|
||||
if (rc && !kdump_in_progress())
|
||||
panic("Could not enable big endian exceptions");
|
||||
}
|
||||
#endif
|
||||
|
@ -3,6 +3,7 @@ config UML
|
||||
default y
|
||||
select HAVE_ARCH_AUDITSYSCALL
|
||||
select HAVE_UID16
|
||||
select HAVE_FUTEX_CMPXCHG if FUTEX
|
||||
select GENERIC_IRQ_SHOW
|
||||
select GENERIC_CPU_DEVICES
|
||||
select GENERIC_IO
|
||||
|
@ -322,7 +322,8 @@ void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
|
||||
* Target instructions MUST be relocatable (checked inside)
|
||||
* This is called when new aggr(opt)probe is allocated or reused.
|
||||
*/
|
||||
int arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
|
||||
int arch_prepare_optimized_kprobe(struct optimized_kprobe *op,
|
||||
struct kprobe *__unused)
|
||||
{
|
||||
u8 *buf;
|
||||
int ret;
|
||||
|
@ -4448,7 +4448,7 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm)
|
||||
* zap all shadow pages.
|
||||
*/
|
||||
if (unlikely(kvm_current_mmio_generation(kvm) == 0)) {
|
||||
printk_ratelimited(KERN_INFO "kvm: zapping shadow pages for mmio generation wraparound\n");
|
||||
printk_ratelimited(KERN_DEBUG "kvm: zapping shadow pages for mmio generation wraparound\n");
|
||||
kvm_mmu_invalidate_zap_all_pages(kvm);
|
||||
}
|
||||
}
|
||||
|
@ -5840,53 +5840,10 @@ static __init int hardware_setup(void)
|
||||
memset(vmx_msr_bitmap_legacy, 0xff, PAGE_SIZE);
|
||||
memset(vmx_msr_bitmap_longmode, 0xff, PAGE_SIZE);
|
||||
|
||||
vmx_disable_intercept_for_msr(MSR_FS_BASE, false);
|
||||
vmx_disable_intercept_for_msr(MSR_GS_BASE, false);
|
||||
vmx_disable_intercept_for_msr(MSR_KERNEL_GS_BASE, true);
|
||||
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_CS, false);
|
||||
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_ESP, false);
|
||||
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_EIP, false);
|
||||
vmx_disable_intercept_for_msr(MSR_IA32_BNDCFGS, true);
|
||||
|
||||
memcpy(vmx_msr_bitmap_legacy_x2apic,
|
||||
vmx_msr_bitmap_legacy, PAGE_SIZE);
|
||||
memcpy(vmx_msr_bitmap_longmode_x2apic,
|
||||
vmx_msr_bitmap_longmode, PAGE_SIZE);
|
||||
|
||||
if (enable_apicv) {
|
||||
for (msr = 0x800; msr <= 0x8ff; msr++)
|
||||
vmx_disable_intercept_msr_read_x2apic(msr);
|
||||
|
||||
/* According SDM, in x2apic mode, the whole id reg is used.
|
||||
* But in KVM, it only use the highest eight bits. Need to
|
||||
* intercept it */
|
||||
vmx_enable_intercept_msr_read_x2apic(0x802);
|
||||
/* TMCCT */
|
||||
vmx_enable_intercept_msr_read_x2apic(0x839);
|
||||
/* TPR */
|
||||
vmx_disable_intercept_msr_write_x2apic(0x808);
|
||||
/* EOI */
|
||||
vmx_disable_intercept_msr_write_x2apic(0x80b);
|
||||
/* SELF-IPI */
|
||||
vmx_disable_intercept_msr_write_x2apic(0x83f);
|
||||
}
|
||||
|
||||
if (enable_ept) {
|
||||
kvm_mmu_set_mask_ptes(0ull,
|
||||
(enable_ept_ad_bits) ? VMX_EPT_ACCESS_BIT : 0ull,
|
||||
(enable_ept_ad_bits) ? VMX_EPT_DIRTY_BIT : 0ull,
|
||||
0ull, VMX_EPT_EXECUTABLE_MASK);
|
||||
ept_set_mmio_spte_mask();
|
||||
kvm_enable_tdp();
|
||||
} else
|
||||
kvm_disable_tdp();
|
||||
|
||||
update_ple_window_actual_max();
|
||||
|
||||
if (setup_vmcs_config(&vmcs_config) < 0) {
|
||||
r = -EIO;
|
||||
goto out7;
|
||||
}
|
||||
}
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_NX))
|
||||
kvm_enable_efer_bits(EFER_NX);
|
||||
@ -5945,6 +5902,49 @@ static __init int hardware_setup(void)
|
||||
if (nested)
|
||||
nested_vmx_setup_ctls_msrs();
|
||||
|
||||
vmx_disable_intercept_for_msr(MSR_FS_BASE, false);
|
||||
vmx_disable_intercept_for_msr(MSR_GS_BASE, false);
|
||||
vmx_disable_intercept_for_msr(MSR_KERNEL_GS_BASE, true);
|
||||
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_CS, false);
|
||||
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_ESP, false);
|
||||
vmx_disable_intercept_for_msr(MSR_IA32_SYSENTER_EIP, false);
|
||||
vmx_disable_intercept_for_msr(MSR_IA32_BNDCFGS, true);
|
||||
|
||||
memcpy(vmx_msr_bitmap_legacy_x2apic,
|
||||
vmx_msr_bitmap_legacy, PAGE_SIZE);
|
||||
memcpy(vmx_msr_bitmap_longmode_x2apic,
|
||||
vmx_msr_bitmap_longmode, PAGE_SIZE);
|
||||
|
||||
if (enable_apicv) {
|
||||
for (msr = 0x800; msr <= 0x8ff; msr++)
|
||||
vmx_disable_intercept_msr_read_x2apic(msr);
|
||||
|
||||
/* According SDM, in x2apic mode, the whole id reg is used.
|
||||
* But in KVM, it only use the highest eight bits. Need to
|
||||
* intercept it */
|
||||
vmx_enable_intercept_msr_read_x2apic(0x802);
|
||||
/* TMCCT */
|
||||
vmx_enable_intercept_msr_read_x2apic(0x839);
|
||||
/* TPR */
|
||||
vmx_disable_intercept_msr_write_x2apic(0x808);
|
||||
/* EOI */
|
||||
vmx_disable_intercept_msr_write_x2apic(0x80b);
|
||||
/* SELF-IPI */
|
||||
vmx_disable_intercept_msr_write_x2apic(0x83f);
|
||||
}
|
||||
|
||||
if (enable_ept) {
|
||||
kvm_mmu_set_mask_ptes(0ull,
|
||||
(enable_ept_ad_bits) ? VMX_EPT_ACCESS_BIT : 0ull,
|
||||
(enable_ept_ad_bits) ? VMX_EPT_DIRTY_BIT : 0ull,
|
||||
0ull, VMX_EPT_EXECUTABLE_MASK);
|
||||
ept_set_mmio_spte_mask();
|
||||
kvm_enable_tdp();
|
||||
} else
|
||||
kvm_disable_tdp();
|
||||
|
||||
update_ple_window_actual_max();
|
||||
|
||||
return alloc_kvm_area();
|
||||
|
||||
out7:
|
||||
|
@ -34,7 +34,7 @@ typedef asmlinkage void (*sys_call_ptr_t)(void);
|
||||
|
||||
extern asmlinkage void sys_ni_syscall(void);
|
||||
|
||||
const sys_call_ptr_t sys_call_table[] __cacheline_aligned = {
|
||||
const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = {
|
||||
/*
|
||||
* Smells like a compiler bug -- it doesn't work
|
||||
* when the & below is removed.
|
||||
|
@ -47,7 +47,7 @@ typedef void (*sys_call_ptr_t)(void);
|
||||
|
||||
extern void sys_ni_syscall(void);
|
||||
|
||||
const sys_call_ptr_t sys_call_table[] __cacheline_aligned = {
|
||||
const sys_call_ptr_t sys_call_table[] ____cacheline_aligned = {
|
||||
/*
|
||||
* Smells like a compiler bug -- it doesn't work
|
||||
* when the & below is removed.
|
||||
|
@ -455,6 +455,9 @@ void af_alg_complete(struct crypto_async_request *req, int err)
|
||||
{
|
||||
struct af_alg_completion *completion = req->data;
|
||||
|
||||
if (err == -EINPROGRESS)
|
||||
return;
|
||||
|
||||
completion->err = err;
|
||||
complete(&completion->completion);
|
||||
}
|
||||
|
@ -985,8 +985,6 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
|
||||
state->flags = 0;
|
||||
switch (cx->type) {
|
||||
case ACPI_STATE_C1:
|
||||
if (cx->entry_method != ACPI_CSTATE_FFH)
|
||||
state->flags |= CPUIDLE_FLAG_TIME_INVALID;
|
||||
|
||||
state->enter = acpi_idle_enter_c1;
|
||||
state->enter_dead = acpi_idle_play_dead;
|
||||
|
@ -505,6 +505,23 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"),
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
.callback = video_disable_native_backlight,
|
||||
.ident = "SAMSUNG 870Z5E/880Z5E/680Z5E",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_disable_native_backlight,
|
||||
.ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "370R4E/370R4V/370R5E/3570RE/370R5V"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -2088,7 +2088,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider);
|
||||
* Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
|
||||
* on failure.
|
||||
*/
|
||||
static struct generic_pm_domain *of_genpd_get_from_provider(
|
||||
struct generic_pm_domain *of_genpd_get_from_provider(
|
||||
struct of_phandle_args *genpdspec)
|
||||
{
|
||||
struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
|
||||
@ -2108,6 +2108,7 @@ static struct generic_pm_domain *of_genpd_get_from_provider(
|
||||
|
||||
return genpd;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_genpd_get_from_provider);
|
||||
|
||||
/**
|
||||
* genpd_dev_pm_detach - Detach a device from its PM domain.
|
||||
|
@ -108,6 +108,14 @@ static LIST_HEAD(dev_opp_list);
|
||||
/* Lock to allow exclusive modification to the device and opp lists */
|
||||
static DEFINE_MUTEX(dev_opp_list_lock);
|
||||
|
||||
#define opp_rcu_lockdep_assert() \
|
||||
do { \
|
||||
rcu_lockdep_assert(rcu_read_lock_held() || \
|
||||
lockdep_is_held(&dev_opp_list_lock), \
|
||||
"Missing rcu_read_lock() or " \
|
||||
"dev_opp_list_lock protection"); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* find_device_opp() - find device_opp struct using device pointer
|
||||
* @dev: device pointer used to lookup device OPPs
|
||||
@ -208,9 +216,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
|
||||
* This function returns the number of available opps if there are any,
|
||||
* else returns 0 if none or the corresponding error value.
|
||||
*
|
||||
* Locking: This function must be called under rcu_read_lock(). This function
|
||||
* internally references two RCU protected structures: device_opp and opp which
|
||||
* are safe as long as we are under a common RCU locked section.
|
||||
* Locking: This function takes rcu_read_lock().
|
||||
*/
|
||||
int dev_pm_opp_get_opp_count(struct device *dev)
|
||||
{
|
||||
@ -218,11 +224,14 @@ int dev_pm_opp_get_opp_count(struct device *dev)
|
||||
struct dev_pm_opp *temp_opp;
|
||||
int count = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
dev_opp = find_device_opp(dev);
|
||||
if (IS_ERR(dev_opp)) {
|
||||
int r = PTR_ERR(dev_opp);
|
||||
dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r);
|
||||
return r;
|
||||
count = PTR_ERR(dev_opp);
|
||||
dev_err(dev, "%s: device OPP not found (%d)\n",
|
||||
__func__, count);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) {
|
||||
@ -230,6 +239,8 @@ int dev_pm_opp_get_opp_count(struct device *dev)
|
||||
count++;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count);
|
||||
@ -267,6 +278,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
|
||||
struct device_opp *dev_opp;
|
||||
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
|
||||
|
||||
opp_rcu_lockdep_assert();
|
||||
|
||||
dev_opp = find_device_opp(dev);
|
||||
if (IS_ERR(dev_opp)) {
|
||||
int r = PTR_ERR(dev_opp);
|
||||
@ -313,6 +326,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
|
||||
struct device_opp *dev_opp;
|
||||
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
|
||||
|
||||
opp_rcu_lockdep_assert();
|
||||
|
||||
if (!dev || !freq) {
|
||||
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
|
||||
return ERR_PTR(-EINVAL);
|
||||
@ -361,6 +376,8 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
|
||||
struct device_opp *dev_opp;
|
||||
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
|
||||
|
||||
opp_rcu_lockdep_assert();
|
||||
|
||||
if (!dev || !freq) {
|
||||
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
|
||||
return ERR_PTR(-EINVAL);
|
||||
@ -783,9 +800,15 @@ void of_free_opp_table(struct device *dev)
|
||||
|
||||
/* Check for existing list for 'dev' */
|
||||
dev_opp = find_device_opp(dev);
|
||||
if (WARN(IS_ERR(dev_opp), "%s: dev_opp: %ld\n", dev_name(dev),
|
||||
PTR_ERR(dev_opp)))
|
||||
if (IS_ERR(dev_opp)) {
|
||||
int error = PTR_ERR(dev_opp);
|
||||
if (error != -ENODEV)
|
||||
WARN(1, "%s: dev_opp: %d\n",
|
||||
IS_ERR_OR_NULL(dev) ?
|
||||
"Invalid device" : dev_name(dev),
|
||||
error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Hold our list modification lock here */
|
||||
mutex_lock(&dev_opp_list_lock);
|
||||
|
@ -417,6 +417,6 @@ static void __exit agp_ali_cleanup(void)
|
||||
module_init(agp_ali_init);
|
||||
module_exit(agp_ali_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
|
||||
MODULE_AUTHOR("Dave Jones");
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
|
||||
|
@ -813,6 +813,6 @@ static void __exit agp_amd64_cleanup(void)
|
||||
module_init(agp_amd64_mod_init);
|
||||
module_exit(agp_amd64_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Dave Jones <davej@redhat.com>, Andi Kleen");
|
||||
MODULE_AUTHOR("Dave Jones, Andi Kleen");
|
||||
module_param(agp_try_unsupported, bool, 0);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -579,6 +579,6 @@ static void __exit agp_ati_cleanup(void)
|
||||
module_init(agp_ati_init);
|
||||
module_exit(agp_ati_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
|
||||
MODULE_AUTHOR("Dave Jones");
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
|
||||
|
@ -356,7 +356,7 @@ static __init int agp_setup(char *s)
|
||||
__setup("agp=", agp_setup);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
|
||||
MODULE_AUTHOR("Dave Jones, Jeff Hartmann");
|
||||
MODULE_DESCRIPTION("AGP GART driver");
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
MODULE_ALIAS_MISCDEV(AGPGART_MINOR);
|
||||
|
@ -920,5 +920,5 @@ static void __exit agp_intel_cleanup(void)
|
||||
module_init(agp_intel_init);
|
||||
module_exit(agp_intel_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
|
||||
MODULE_AUTHOR("Dave Jones, Various @Intel");
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
|
@ -1438,5 +1438,5 @@ void intel_gmch_remove(void)
|
||||
}
|
||||
EXPORT_SYMBOL(intel_gmch_remove);
|
||||
|
||||
MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
|
||||
MODULE_AUTHOR("Dave Jones, Various @Intel");
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Nvidia AGPGART routines.
|
||||
* Based upon a 2.4 agpgart diff by the folks from NVIDIA, and hacked up
|
||||
* to work in 2.5 by Dave Jones <davej@redhat.com>
|
||||
* to work in 2.5 by Dave Jones.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -595,4 +595,4 @@ module_init(agp_via_init);
|
||||
module_exit(agp_via_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Dave Jones <davej@redhat.com>");
|
||||
MODULE_AUTHOR("Dave Jones");
|
||||
|
@ -199,18 +199,6 @@ struct bmc_device {
|
||||
int guid_set;
|
||||
char name[16];
|
||||
struct kref usecount;
|
||||
|
||||
/* bmc device attributes */
|
||||
struct device_attribute device_id_attr;
|
||||
struct device_attribute provides_dev_sdrs_attr;
|
||||
struct device_attribute revision_attr;
|
||||
struct device_attribute firmware_rev_attr;
|
||||
struct device_attribute version_attr;
|
||||
struct device_attribute add_dev_support_attr;
|
||||
struct device_attribute manufacturer_id_attr;
|
||||
struct device_attribute product_id_attr;
|
||||
struct device_attribute guid_attr;
|
||||
struct device_attribute aux_firmware_rev_attr;
|
||||
};
|
||||
#define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev)
|
||||
|
||||
@ -2252,7 +2240,7 @@ static ssize_t device_id_show(struct device *dev,
|
||||
|
||||
return snprintf(buf, 10, "%u\n", bmc->id.device_id);
|
||||
}
|
||||
DEVICE_ATTR(device_id, S_IRUGO, device_id_show, NULL);
|
||||
static DEVICE_ATTR(device_id, S_IRUGO, device_id_show, NULL);
|
||||
|
||||
static ssize_t provides_device_sdrs_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -2263,7 +2251,8 @@ static ssize_t provides_device_sdrs_show(struct device *dev,
|
||||
return snprintf(buf, 10, "%u\n",
|
||||
(bmc->id.device_revision & 0x80) >> 7);
|
||||
}
|
||||
DEVICE_ATTR(provides_device_sdrs, S_IRUGO, provides_device_sdrs_show, NULL);
|
||||
static DEVICE_ATTR(provides_device_sdrs, S_IRUGO, provides_device_sdrs_show,
|
||||
NULL);
|
||||
|
||||
static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -2273,7 +2262,7 @@ static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
|
||||
return snprintf(buf, 20, "%u\n",
|
||||
bmc->id.device_revision & 0x0F);
|
||||
}
|
||||
DEVICE_ATTR(revision, S_IRUGO, revision_show, NULL);
|
||||
static DEVICE_ATTR(revision, S_IRUGO, revision_show, NULL);
|
||||
|
||||
static ssize_t firmware_revision_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -2284,7 +2273,7 @@ static ssize_t firmware_revision_show(struct device *dev,
|
||||
return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1,
|
||||
bmc->id.firmware_revision_2);
|
||||
}
|
||||
DEVICE_ATTR(firmware_revision, S_IRUGO, firmware_revision_show, NULL);
|
||||
static DEVICE_ATTR(firmware_revision, S_IRUGO, firmware_revision_show, NULL);
|
||||
|
||||
static ssize_t ipmi_version_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -2296,7 +2285,7 @@ static ssize_t ipmi_version_show(struct device *dev,
|
||||
ipmi_version_major(&bmc->id),
|
||||
ipmi_version_minor(&bmc->id));
|
||||
}
|
||||
DEVICE_ATTR(ipmi_version, S_IRUGO, ipmi_version_show, NULL);
|
||||
static DEVICE_ATTR(ipmi_version, S_IRUGO, ipmi_version_show, NULL);
|
||||
|
||||
static ssize_t add_dev_support_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -2307,7 +2296,8 @@ static ssize_t add_dev_support_show(struct device *dev,
|
||||
return snprintf(buf, 10, "0x%02x\n",
|
||||
bmc->id.additional_device_support);
|
||||
}
|
||||
DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show, NULL);
|
||||
static DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show,
|
||||
NULL);
|
||||
|
||||
static ssize_t manufacturer_id_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -2317,7 +2307,7 @@ static ssize_t manufacturer_id_show(struct device *dev,
|
||||
|
||||
return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id);
|
||||
}
|
||||
DEVICE_ATTR(manufacturer_id, S_IRUGO, manufacturer_id_show, NULL);
|
||||
static DEVICE_ATTR(manufacturer_id, S_IRUGO, manufacturer_id_show, NULL);
|
||||
|
||||
static ssize_t product_id_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -2327,7 +2317,7 @@ static ssize_t product_id_show(struct device *dev,
|
||||
|
||||
return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id);
|
||||
}
|
||||
DEVICE_ATTR(product_id, S_IRUGO, product_id_show, NULL);
|
||||
static DEVICE_ATTR(product_id, S_IRUGO, product_id_show, NULL);
|
||||
|
||||
static ssize_t aux_firmware_rev_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -2341,7 +2331,7 @@ static ssize_t aux_firmware_rev_show(struct device *dev,
|
||||
bmc->id.aux_firmware_revision[1],
|
||||
bmc->id.aux_firmware_revision[0]);
|
||||
}
|
||||
DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL);
|
||||
static DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL);
|
||||
|
||||
static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
@ -2352,7 +2342,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
|
||||
(long long) bmc->guid[0],
|
||||
(long long) bmc->guid[8]);
|
||||
}
|
||||
DEVICE_ATTR(guid, S_IRUGO, guid_show, NULL);
|
||||
static DEVICE_ATTR(guid, S_IRUGO, guid_show, NULL);
|
||||
|
||||
static struct attribute *bmc_dev_attrs[] = {
|
||||
&dev_attr_device_id.attr,
|
||||
@ -2392,10 +2382,10 @@ cleanup_bmc_device(struct kref *ref)
|
||||
|
||||
if (bmc->id.aux_firmware_revision_set)
|
||||
device_remove_file(&bmc->pdev.dev,
|
||||
&bmc->aux_firmware_rev_attr);
|
||||
&dev_attr_aux_firmware_revision);
|
||||
if (bmc->guid_set)
|
||||
device_remove_file(&bmc->pdev.dev,
|
||||
&bmc->guid_attr);
|
||||
&dev_attr_guid);
|
||||
|
||||
platform_device_unregister(&bmc->pdev);
|
||||
}
|
||||
@ -2422,16 +2412,14 @@ static int create_bmc_files(struct bmc_device *bmc)
|
||||
int err;
|
||||
|
||||
if (bmc->id.aux_firmware_revision_set) {
|
||||
bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision";
|
||||
err = device_create_file(&bmc->pdev.dev,
|
||||
&bmc->aux_firmware_rev_attr);
|
||||
&dev_attr_aux_firmware_revision);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
if (bmc->guid_set) {
|
||||
bmc->guid_attr.attr.name = "guid";
|
||||
err = device_create_file(&bmc->pdev.dev,
|
||||
&bmc->guid_attr);
|
||||
&dev_attr_guid);
|
||||
if (err)
|
||||
goto out_aux_firm;
|
||||
}
|
||||
@ -2441,7 +2429,7 @@ static int create_bmc_files(struct bmc_device *bmc)
|
||||
out_aux_firm:
|
||||
if (bmc->id.aux_firmware_revision_set)
|
||||
device_remove_file(&bmc->pdev.dev,
|
||||
&bmc->aux_firmware_rev_attr);
|
||||
&dev_attr_aux_firmware_revision);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
#define PFX "ipmi_ssif: "
|
||||
#define DEVICE_NAME "ipmi_ssif"
|
||||
|
@ -462,7 +462,7 @@ static void __init arch_counter_register(unsigned type)
|
||||
|
||||
/* Register the CP15 based counter if we have one */
|
||||
if (type & ARCH_CP15_TIMER) {
|
||||
if (arch_timer_use_virtual)
|
||||
if (IS_ENABLED(CONFIG_ARM64) || arch_timer_use_virtual)
|
||||
arch_timer_read_counter = arch_counter_get_cntvct;
|
||||
else
|
||||
arch_timer_read_counter = arch_counter_get_cntpct;
|
||||
|
@ -211,6 +211,17 @@ static int cpufreq_init(struct cpufreq_policy *policy)
|
||||
/* OPPs might be populated at runtime, don't check for error here */
|
||||
of_init_opp_table(cpu_dev);
|
||||
|
||||
/*
|
||||
* But we need OPP table to function so if it is not there let's
|
||||
* give platform code chance to provide it for us.
|
||||
*/
|
||||
ret = dev_pm_opp_get_opp_count(cpu_dev);
|
||||
if (ret <= 0) {
|
||||
pr_debug("OPP table is not ready, deferring probe\n");
|
||||
ret = -EPROBE_DEFER;
|
||||
goto out_free_opp;
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
|
@ -2028,6 +2028,12 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
|
||||
/* Don't start any governor operations if we are entering suspend */
|
||||
if (cpufreq_suspended)
|
||||
return 0;
|
||||
/*
|
||||
* Governor might not be initiated here if ACPI _PPC changed
|
||||
* notification happened, so check it.
|
||||
*/
|
||||
if (!policy->governor)
|
||||
return -EINVAL;
|
||||
|
||||
if (policy->governor->max_transition_latency &&
|
||||
policy->cpuinfo.transition_latency >
|
||||
|
@ -79,12 +79,7 @@ static int ladder_select_state(struct cpuidle_driver *drv,
|
||||
|
||||
last_state = &ldev->states[last_idx];
|
||||
|
||||
if (!(drv->states[last_idx].flags & CPUIDLE_FLAG_TIME_INVALID)) {
|
||||
last_residency = cpuidle_get_last_residency(dev) - \
|
||||
drv->states[last_idx].exit_latency;
|
||||
}
|
||||
else
|
||||
last_residency = last_state->threshold.promotion_time + 1;
|
||||
last_residency = cpuidle_get_last_residency(dev) - drv->states[last_idx].exit_latency;
|
||||
|
||||
/* consider promotion */
|
||||
if (last_idx < drv->state_count - 1 &&
|
||||
|
@ -396,8 +396,8 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
|
||||
* power state and occurrence of the wakeup event.
|
||||
*
|
||||
* If the entered idle state didn't support residency measurements,
|
||||
* we are basically lost in the dark how much time passed.
|
||||
* As a compromise, assume we slept for the whole expected time.
|
||||
* we use them anyway if they are short, and if long,
|
||||
* truncate to the whole expected time.
|
||||
*
|
||||
* Any measured amount of time will include the exit latency.
|
||||
* Since we are interested in when the wakeup begun, not when it
|
||||
@ -405,23 +405,18 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
|
||||
* the measured amount of time is less than the exit latency,
|
||||
* assume the state was never reached and the exit latency is 0.
|
||||
*/
|
||||
if (unlikely(target->flags & CPUIDLE_FLAG_TIME_INVALID)) {
|
||||
/* Use timer value as is */
|
||||
|
||||
/* measured value */
|
||||
measured_us = cpuidle_get_last_residency(dev);
|
||||
|
||||
/* Deduct exit latency */
|
||||
if (measured_us > target->exit_latency)
|
||||
measured_us -= target->exit_latency;
|
||||
|
||||
/* Make sure our coefficients do not exceed unity */
|
||||
if (measured_us > data->next_timer_us)
|
||||
measured_us = data->next_timer_us;
|
||||
|
||||
} else {
|
||||
/* Use measured value */
|
||||
measured_us = cpuidle_get_last_residency(dev);
|
||||
|
||||
/* Deduct exit latency */
|
||||
if (measured_us > target->exit_latency)
|
||||
measured_us -= target->exit_latency;
|
||||
|
||||
/* Make sure our coefficients do not exceed unity */
|
||||
if (measured_us > data->next_timer_us)
|
||||
measured_us = data->next_timer_us;
|
||||
}
|
||||
|
||||
/* Update our correction ratio */
|
||||
new_factor = data->correction_factor[data->bucket];
|
||||
new_factor -= new_factor / DECAY;
|
||||
|
@ -121,13 +121,9 @@ static int kfd_open(struct inode *inode, struct file *filep)
|
||||
if (IS_ERR(process))
|
||||
return PTR_ERR(process);
|
||||
|
||||
process->is_32bit_user_mode = is_32bit_user_mode;
|
||||
|
||||
dev_dbg(kfd_device, "process %d opened, compat mode (32 bit) - %d\n",
|
||||
process->pasid, process->is_32bit_user_mode);
|
||||
|
||||
kfd_init_apertures(process);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -299,13 +299,13 @@ int kfd_init_apertures(struct kfd_process *process)
|
||||
struct kfd_dev *dev;
|
||||
struct kfd_process_device *pdd;
|
||||
|
||||
mutex_lock(&process->mutex);
|
||||
|
||||
/*Iterating over all devices*/
|
||||
while ((dev = kfd_topology_enum_kfd_devices(id)) != NULL &&
|
||||
id < NUM_OF_SUPPORTED_GPUS) {
|
||||
|
||||
pdd = kfd_get_process_device_data(dev, process, 1);
|
||||
if (!pdd)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* For 64 bit process aperture will be statically reserved in
|
||||
@ -348,8 +348,6 @@ int kfd_init_apertures(struct kfd_process *process)
|
||||
id++;
|
||||
}
|
||||
|
||||
mutex_unlock(&process->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/amd-iommu.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
struct mm_struct;
|
||||
|
||||
#include "kfd_priv.h"
|
||||
@ -285,8 +287,15 @@ static struct kfd_process *create_process(const struct task_struct *thread)
|
||||
if (err != 0)
|
||||
goto err_process_pqm_init;
|
||||
|
||||
/* init process apertures*/
|
||||
process->is_32bit_user_mode = is_compat_task();
|
||||
if (kfd_init_apertures(process) != 0)
|
||||
goto err_init_apretures;
|
||||
|
||||
return process;
|
||||
|
||||
err_init_apretures:
|
||||
pqm_uninit(&process->pqm);
|
||||
err_process_pqm_init:
|
||||
hash_del_rcu(&process->kfd_processes);
|
||||
synchronize_rcu();
|
||||
|
@ -700,8 +700,6 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
|
||||
dev->node_props.simd_per_cu);
|
||||
sysfs_show_32bit_prop(buffer, "max_slots_scratch_cu",
|
||||
dev->node_props.max_slots_scratch_cu);
|
||||
sysfs_show_32bit_prop(buffer, "engine_id",
|
||||
dev->node_props.engine_id);
|
||||
sysfs_show_32bit_prop(buffer, "vendor_id",
|
||||
dev->node_props.vendor_id);
|
||||
sysfs_show_32bit_prop(buffer, "device_id",
|
||||
@ -715,6 +713,12 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
|
||||
dev->gpu->kgd));
|
||||
sysfs_show_64bit_prop(buffer, "local_mem_size",
|
||||
kfd2kgd->get_vmem_size(dev->gpu->kgd));
|
||||
|
||||
sysfs_show_32bit_prop(buffer, "fw_version",
|
||||
kfd2kgd->get_fw_version(
|
||||
dev->gpu->kgd,
|
||||
KGD_ENGINE_MEC1));
|
||||
|
||||
}
|
||||
|
||||
ret = sysfs_show_32bit_prop(buffer, "max_engine_clk_ccompute",
|
||||
|
@ -45,6 +45,17 @@ enum kgd_memory_pool {
|
||||
KGD_POOL_FRAMEBUFFER = 3,
|
||||
};
|
||||
|
||||
enum kgd_engine_type {
|
||||
KGD_ENGINE_PFP = 1,
|
||||
KGD_ENGINE_ME,
|
||||
KGD_ENGINE_CE,
|
||||
KGD_ENGINE_MEC1,
|
||||
KGD_ENGINE_MEC2,
|
||||
KGD_ENGINE_RLC,
|
||||
KGD_ENGINE_SDMA,
|
||||
KGD_ENGINE_MAX
|
||||
};
|
||||
|
||||
struct kgd2kfd_shared_resources {
|
||||
/* Bit n == 1 means VMID n is available for KFD. */
|
||||
unsigned int compute_vmid_bitmap;
|
||||
@ -137,6 +148,8 @@ struct kgd2kfd_calls {
|
||||
*
|
||||
* @hqd_destroy: Destructs and preempts the queue assigned to that hqd slot.
|
||||
*
|
||||
* @get_fw_version: Returns FW versions from the header
|
||||
*
|
||||
* This structure contains function pointers to services that the kgd driver
|
||||
* provides to amdkfd driver.
|
||||
*
|
||||
@ -176,6 +189,8 @@ struct kfd2kgd_calls {
|
||||
int (*hqd_destroy)(struct kgd_dev *kgd, uint32_t reset_type,
|
||||
unsigned int timeout, uint32_t pipe_id,
|
||||
uint32_t queue_id);
|
||||
uint16_t (*get_fw_version)(struct kgd_dev *kgd,
|
||||
enum kgd_engine_type type);
|
||||
};
|
||||
|
||||
bool kgd2kfd_init(unsigned interface_version,
|
||||
|
@ -61,7 +61,7 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
|
||||
struct drm_crtc_state *crtc_state;
|
||||
|
||||
if (plane->state->crtc) {
|
||||
crtc_state = state->crtc_states[drm_crtc_index(plane->crtc)];
|
||||
crtc_state = state->crtc_states[drm_crtc_index(plane->state->crtc)];
|
||||
|
||||
if (WARN_ON(!crtc_state))
|
||||
return;
|
||||
|
@ -830,6 +830,8 @@ drm_get_last_vbltimestamp(struct drm_device *dev, int crtc,
|
||||
* vblank events since the system was booted, including lost events due to
|
||||
* modesetting activity.
|
||||
*
|
||||
* This is the legacy version of drm_crtc_vblank_count().
|
||||
*
|
||||
* Returns:
|
||||
* The software vblank counter.
|
||||
*/
|
||||
@ -843,6 +845,25 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_vblank_count);
|
||||
|
||||
/**
|
||||
* drm_crtc_vblank_count - retrieve "cooked" vblank counter value
|
||||
* @crtc: which counter to retrieve
|
||||
*
|
||||
* Fetches the "cooked" vblank count value that represents the number of
|
||||
* vblank events since the system was booted, including lost events due to
|
||||
* modesetting activity.
|
||||
*
|
||||
* This is the native KMS version of drm_vblank_count().
|
||||
*
|
||||
* Returns:
|
||||
* The software vblank counter.
|
||||
*/
|
||||
u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
|
||||
{
|
||||
return drm_vblank_count(crtc->dev, drm_crtc_index(crtc));
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_vblank_count);
|
||||
|
||||
/**
|
||||
* drm_vblank_count_and_time - retrieve "cooked" vblank counter value
|
||||
* and the system timestamp corresponding to that vblank counter value.
|
||||
@ -904,6 +925,8 @@ static void send_vblank_event(struct drm_device *dev,
|
||||
*
|
||||
* Updates sequence # and timestamp on event, and sends it to userspace.
|
||||
* Caller must hold event lock.
|
||||
*
|
||||
* This is the legacy version of drm_crtc_send_vblank_event().
|
||||
*/
|
||||
void drm_send_vblank_event(struct drm_device *dev, int crtc,
|
||||
struct drm_pending_vblank_event *e)
|
||||
@ -922,6 +945,23 @@ void drm_send_vblank_event(struct drm_device *dev, int crtc,
|
||||
}
|
||||
EXPORT_SYMBOL(drm_send_vblank_event);
|
||||
|
||||
/**
|
||||
* drm_crtc_send_vblank_event - helper to send vblank event after pageflip
|
||||
* @crtc: the source CRTC of the vblank event
|
||||
* @e: the event to send
|
||||
*
|
||||
* Updates sequence # and timestamp on event, and sends it to userspace.
|
||||
* Caller must hold event lock.
|
||||
*
|
||||
* This is the native KMS version of drm_send_vblank_event().
|
||||
*/
|
||||
void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
|
||||
struct drm_pending_vblank_event *e)
|
||||
{
|
||||
drm_send_vblank_event(crtc->dev, drm_crtc_index(crtc), e);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_send_vblank_event);
|
||||
|
||||
/**
|
||||
* drm_vblank_enable - enable the vblank interrupt on a CRTC
|
||||
* @dev: DRM device
|
||||
@ -1594,6 +1634,8 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
|
||||
*
|
||||
* Drivers should call this routine in their vblank interrupt handlers to
|
||||
* update the vblank counter and send any signals that may be pending.
|
||||
*
|
||||
* This is the legacy version of drm_crtc_handle_vblank().
|
||||
*/
|
||||
bool drm_handle_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
@ -1670,3 +1712,21 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_handle_vblank);
|
||||
|
||||
/**
|
||||
* drm_crtc_handle_vblank - handle a vblank event
|
||||
* @crtc: where this event occurred
|
||||
*
|
||||
* Drivers should call this routine in their vblank interrupt handlers to
|
||||
* update the vblank counter and send any signals that may be pending.
|
||||
*
|
||||
* This is the native KMS version of drm_handle_vblank().
|
||||
*
|
||||
* Returns:
|
||||
* True if the event was successfully handled, false on failure.
|
||||
*/
|
||||
bool drm_crtc_handle_vblank(struct drm_crtc *crtc)
|
||||
{
|
||||
return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc));
|
||||
}
|
||||
EXPORT_SYMBOL(drm_crtc_handle_vblank);
|
||||
|
@ -811,6 +811,8 @@ int i915_reset(struct drm_device *dev)
|
||||
if (!i915.reset)
|
||||
return 0;
|
||||
|
||||
intel_reset_gt_powersave(dev);
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
i915_gem_reset(dev);
|
||||
@ -880,7 +882,7 @@ int i915_reset(struct drm_device *dev)
|
||||
* of re-init after reset.
|
||||
*/
|
||||
if (INTEL_INFO(dev)->gen > 5)
|
||||
intel_reset_gt_powersave(dev);
|
||||
intel_enable_gt_powersave(dev);
|
||||
} else {
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
||||
@ -1584,7 +1586,7 @@ static struct drm_driver driver = {
|
||||
.gem_prime_import = i915_gem_prime_import,
|
||||
|
||||
.dumb_create = i915_gem_dumb_create,
|
||||
.dumb_map_offset = i915_gem_dumb_map_offset,
|
||||
.dumb_map_offset = i915_gem_mmap_gtt,
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
.ioctls = i915_ioctls,
|
||||
.fops = &i915_driver_fops,
|
||||
|
@ -2501,9 +2501,8 @@ void i915_vma_move_to_active(struct i915_vma *vma,
|
||||
int i915_gem_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
int i915_gem_dumb_map_offset(struct drm_file *file_priv,
|
||||
struct drm_device *dev, uint32_t handle,
|
||||
uint64_t *offset);
|
||||
int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev,
|
||||
uint32_t handle, uint64_t *offset);
|
||||
/**
|
||||
* Returns true if seq1 is later than seq2.
|
||||
*/
|
||||
|
@ -401,7 +401,6 @@ static int
|
||||
i915_gem_create(struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
uint64_t size,
|
||||
bool dumb,
|
||||
uint32_t *handle_p)
|
||||
{
|
||||
struct drm_i915_gem_object *obj;
|
||||
@ -417,7 +416,6 @@ i915_gem_create(struct drm_file *file,
|
||||
if (obj == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
obj->base.dumb = dumb;
|
||||
ret = drm_gem_handle_create(file, &obj->base, &handle);
|
||||
/* drop reference from allocate - handle holds it now */
|
||||
drm_gem_object_unreference_unlocked(&obj->base);
|
||||
@ -437,7 +435,7 @@ i915_gem_dumb_create(struct drm_file *file,
|
||||
args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 64);
|
||||
args->size = args->pitch * args->height;
|
||||
return i915_gem_create(file, dev,
|
||||
args->size, true, &args->handle);
|
||||
args->size, &args->handle);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -450,7 +448,7 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_i915_gem_create *args = data;
|
||||
|
||||
return i915_gem_create(file, dev,
|
||||
args->size, false, &args->handle);
|
||||
args->size, &args->handle);
|
||||
}
|
||||
|
||||
static inline int
|
||||
@ -1840,10 +1838,10 @@ static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
|
||||
drm_gem_free_mmap_offset(&obj->base);
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
i915_gem_mmap_gtt(struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
uint32_t handle, bool dumb,
|
||||
uint32_t handle,
|
||||
uint64_t *offset)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
@ -1860,13 +1858,6 @@ i915_gem_mmap_gtt(struct drm_file *file,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't allow dumb mmaps on objects created using another
|
||||
* interface.
|
||||
*/
|
||||
WARN_ONCE(dumb && !(obj->base.dumb || obj->base.import_attach),
|
||||
"Illegal dumb map of accelerated buffer.\n");
|
||||
|
||||
if (obj->base.size > dev_priv->gtt.mappable_end) {
|
||||
ret = -E2BIG;
|
||||
goto out;
|
||||
@ -1891,15 +1882,6 @@ unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
i915_gem_dumb_map_offset(struct drm_file *file,
|
||||
struct drm_device *dev,
|
||||
uint32_t handle,
|
||||
uint64_t *offset)
|
||||
{
|
||||
return i915_gem_mmap_gtt(file, dev, handle, true, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_gem_mmap_gtt_ioctl - prepare an object for GTT mmap'ing
|
||||
* @dev: DRM device
|
||||
@ -1921,7 +1903,7 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data,
|
||||
{
|
||||
struct drm_i915_gem_mmap_gtt *args = data;
|
||||
|
||||
return i915_gem_mmap_gtt(file, dev, args->handle, false, &args->offset);
|
||||
return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset);
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
@ -473,7 +473,12 @@ mi_set_context(struct intel_engine_cs *ring,
|
||||
u32 hw_flags)
|
||||
{
|
||||
u32 flags = hw_flags | MI_MM_SPACE_GTT;
|
||||
int ret;
|
||||
const int num_rings =
|
||||
/* Use an extended w/a on ivb+ if signalling from other rings */
|
||||
i915_semaphore_is_enabled(ring->dev) ?
|
||||
hweight32(INTEL_INFO(ring->dev)->ring_mask) - 1 :
|
||||
0;
|
||||
int len, i, ret;
|
||||
|
||||
/* w/a: If Flush TLB Invalidation Mode is enabled, driver must do a TLB
|
||||
* invalidation prior to MI_SET_CONTEXT. On GEN6 we don't set the value
|
||||
@ -490,15 +495,31 @@ mi_set_context(struct intel_engine_cs *ring,
|
||||
if (!IS_HASWELL(ring->dev) && INTEL_INFO(ring->dev)->gen < 8)
|
||||
flags |= (MI_SAVE_EXT_STATE_EN | MI_RESTORE_EXT_STATE_EN);
|
||||
|
||||
ret = intel_ring_begin(ring, 6);
|
||||
|
||||
len = 4;
|
||||
if (INTEL_INFO(ring->dev)->gen >= 7)
|
||||
len += 2 + (num_rings ? 4*num_rings + 2 : 0);
|
||||
|
||||
ret = intel_ring_begin(ring, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw,bdw,chv */
|
||||
if (INTEL_INFO(ring->dev)->gen >= 7)
|
||||
if (INTEL_INFO(ring->dev)->gen >= 7) {
|
||||
intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE);
|
||||
else
|
||||
intel_ring_emit(ring, MI_NOOP);
|
||||
if (num_rings) {
|
||||
struct intel_engine_cs *signaller;
|
||||
|
||||
intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(num_rings));
|
||||
for_each_ring(signaller, to_i915(ring->dev), i) {
|
||||
if (signaller == ring)
|
||||
continue;
|
||||
|
||||
intel_ring_emit(ring, RING_PSMI_CTL(signaller->mmio_base));
|
||||
intel_ring_emit(ring, _MASKED_BIT_ENABLE(GEN6_PSMI_SLEEP_MSG_DISABLE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intel_ring_emit(ring, MI_NOOP);
|
||||
intel_ring_emit(ring, MI_SET_CONTEXT);
|
||||
@ -510,10 +531,21 @@ mi_set_context(struct intel_engine_cs *ring,
|
||||
*/
|
||||
intel_ring_emit(ring, MI_NOOP);
|
||||
|
||||
if (INTEL_INFO(ring->dev)->gen >= 7)
|
||||
if (INTEL_INFO(ring->dev)->gen >= 7) {
|
||||
if (num_rings) {
|
||||
struct intel_engine_cs *signaller;
|
||||
|
||||
intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(num_rings));
|
||||
for_each_ring(signaller, to_i915(ring->dev), i) {
|
||||
if (signaller == ring)
|
||||
continue;
|
||||
|
||||
intel_ring_emit(ring, RING_PSMI_CTL(signaller->mmio_base));
|
||||
intel_ring_emit(ring, _MASKED_BIT_DISABLE(GEN6_PSMI_SLEEP_MSG_DISABLE));
|
||||
}
|
||||
}
|
||||
intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_ENABLE);
|
||||
else
|
||||
intel_ring_emit(ring, MI_NOOP);
|
||||
}
|
||||
|
||||
intel_ring_advance(ring);
|
||||
|
||||
|
@ -121,9 +121,6 @@ eb_lookup_vmas(struct eb_vmas *eb,
|
||||
goto err;
|
||||
}
|
||||
|
||||
WARN_ONCE(obj->base.dumb,
|
||||
"GPU use of dumb buffer is illegal.\n");
|
||||
|
||||
drm_gem_object_reference(&obj->base);
|
||||
list_add_tail(&obj->obj_exec_link, &objects);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user