diff --git a/arch/arm64/include/asm/kprobes.h b/arch/arm64/include/asm/kprobes.h
index 79c9511..61b4915 100644
--- a/arch/arm64/include/asm/kprobes.h
+++ b/arch/arm64/include/asm/kprobes.h
@@ -56,5 +56,7 @@ int kprobe_exceptions_notify(struct notifier_block *self,
 			     unsigned long val, void *data);
 int kprobe_breakpoint_handler(struct pt_regs *regs, unsigned int esr);
 int kprobe_single_step_handler(struct pt_regs *regs, unsigned int esr);
+void kretprobe_trampoline(void);
+void __kprobes *trampoline_probe_handler(struct pt_regs *regs);
 
 #endif /* _ARM_KPROBES_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 08325e5..f192b7d 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -37,6 +37,7 @@ arm64-obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
 arm64-obj-$(CONFIG_JUMP_LABEL)		+= jump_label.o
 arm64-obj-$(CONFIG_KGDB)		+= kgdb.o
 arm64-obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes-arm64.o		\
+					   kprobes_trampoline.o			\
 					   probes-simulate-insn.o
 arm64-obj-$(CONFIG_EFI)			+= efi.o efi-entry.stub.o
 arm64-obj-$(CONFIG_PCI)			+= pci.o
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index fffa4ac6..f7cc8ce 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -50,6 +50,17 @@ int main(void)
   DEFINE(S_X5,			offsetof(struct pt_regs, regs[5]));
   DEFINE(S_X6,			offsetof(struct pt_regs, regs[6]));
   DEFINE(S_X7,			offsetof(struct pt_regs, regs[7]));
+  DEFINE(S_X8,			offsetof(struct pt_regs, regs[8]));
+  DEFINE(S_X10,			offsetof(struct pt_regs, regs[10]));
+  DEFINE(S_X12,			offsetof(struct pt_regs, regs[12]));
+  DEFINE(S_X14,			offsetof(struct pt_regs, regs[14]));
+  DEFINE(S_X16,			offsetof(struct pt_regs, regs[16]));
+  DEFINE(S_X18,			offsetof(struct pt_regs, regs[18]));
+  DEFINE(S_X20,			offsetof(struct pt_regs, regs[20]));
+  DEFINE(S_X22,			offsetof(struct pt_regs, regs[22]));
+  DEFINE(S_X24,			offsetof(struct pt_regs, regs[24]));
+  DEFINE(S_X26,			offsetof(struct pt_regs, regs[26]));
+  DEFINE(S_X28,			offsetof(struct pt_regs, regs[28]));
   DEFINE(S_LR,			offsetof(struct pt_regs, regs[30]));
   DEFINE(S_SP,			offsetof(struct pt_regs, sp));
 #ifdef CONFIG_COMPAT
diff --git a/arch/arm64/kernel/kprobes.c b/arch/arm64/kernel/kprobes.c
index ffc5affd..bd3f233 100644
--- a/arch/arm64/kernel/kprobes.c
+++ b/arch/arm64/kernel/kprobes.c
@@ -532,6 +532,11 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
 	return 1;
 }
 
+void __kprobes __used *trampoline_probe_handler(struct pt_regs *regs)
+{
+	return NULL;
+}
+
 int __init arch_init_kprobes(void)
 {
 	return 0;
diff --git a/arch/arm64/kernel/kprobes_trampoline.S b/arch/arm64/kernel/kprobes_trampoline.S
new file mode 100644
index 0000000..072b4e5
--- /dev/null
+++ b/arch/arm64/kernel/kprobes_trampoline.S
@@ -0,0 +1,88 @@
+/*
+ * trampoline entry and return code for kretprobes.
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/assembler.h>
+
+	.text
+
+.macro save_all_base_regs ctxt
+	stp x0, x1, [\ctxt, #S_X0]
+	stp x2, x3, [\ctxt, #S_X2]
+	stp x4, x5, [\ctxt, #S_X4]
+	stp x6, x7, [\ctxt, #S_X6]
+	stp x8, x9, [\ctxt, #S_X8]
+	stp x10, x11, [\ctxt, #S_X10]
+	stp x12, x13, [\ctxt, #S_X12]
+	stp x14, x15, [\ctxt, #S_X14]
+	stp x16, x17, [\ctxt, #S_X16]
+	stp x18, x19, [\ctxt, #S_X18]
+	stp x20, x21, [\ctxt, #S_X20]
+	stp x22, x23, [\ctxt, #S_X22]
+	stp x24, x25, [\ctxt, #S_X24]
+	stp x26, x27, [\ctxt, #S_X26]
+	stp x28, x29, [\ctxt, #S_X28]
+	str lr, [\ctxt, #S_LR]
+	add x0, \ctxt, #S_FRAME_SIZE
+	str x0, [\ctxt, #S_SP]
+/*
+ * Construct a useful saved PSTATE
+ */
+	mrs x0, nzcv
+	and x0, x0, #0xf0000000
+	mrs x1, daif
+	and x1, x1, #0x3c0
+	orr x0, x0, x1
+	mrs x1, CurrentEL
+	and x1, x1, #12
+	lsl x1, x1, #21
+	orr x0, x1, x0
+	mrs x1, SPSel
+	and x1, x1, #1
+	lsl x1, x1, #21
+	orr x0, x1, x0
+	str x0, [\ctxt, #S_PSTATE]
+.endm
+
+.macro restore_all_base_regs ctxt
+	ldr x0, [\ctxt, #S_PSTATE]
+	and x0, x0, #0xf0000000
+	msr nzcv, x0
+	ldp x0, x1, [\ctxt, #S_X0]
+	ldp x2, x3, [\ctxt, #S_X2]
+	ldp x4, x5, [\ctxt, #S_X4]
+	ldp x6, x7, [\ctxt, #S_X6]
+	ldp x8, x9, [\ctxt, #S_X8]
+	ldp x10, x11, [\ctxt, #S_X10]
+	ldp x12, x13, [\ctxt, #S_X12]
+	ldp x14, x15, [\ctxt, #S_X14]
+	ldp x16, x17, [\ctxt, #S_X16]
+	ldp x18, x19, [\ctxt, #S_X18]
+	ldp x20, x21, [\ctxt, #S_X20]
+	ldp x22, x23, [\ctxt, #S_X22]
+	ldp x24, x25, [\ctxt, #S_X24]
+	ldp x26, x27, [\ctxt, #S_X26]
+	ldp x28, x29, [\ctxt, #S_X28]
+.endm
+
+ENTRY(kretprobe_trampoline)
+
+	sub sp, sp, #S_FRAME_SIZE
+
+	save_all_base_regs sp
+
+	mov x0, sp
+	bl trampoline_probe_handler
+	/* Replace trampoline address in lr with actual
+	   orig_ret_addr return address. */
+	mov lr, x0
+
+	restore_all_base_regs sp
+
+	add sp, sp, #S_FRAME_SIZE
+
+	ret
+
+ENDPROC(kretprobe_trampoline)
