diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-08 05:34:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-08 05:34:24 -0400 |
commit | 6325e940e7e0c690c6bdfaf5d54309e71845d3d9 (patch) | |
tree | bd1d2c33ae9420e98d3feee1f924fdad3f22552f /arch/arm64/kernel | |
parent | 536fd93d432858eb6b7c1ad1dcfe051840ebef47 (diff) | |
parent | 0a6479b0ffad8dd236915e271faaf2cbb4cac287 (diff) |
Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
Pull arm64 updates from Catalin Marinas:
- eBPF JIT compiler for arm64
- CPU suspend backend for PSCI (firmware interface) with standard idle
states defined in DT (generic idle driver to be merged via a
different tree)
- Support for CONFIG_DEBUG_SET_MODULE_RONX
- Support for unmapped cpu-release-addr (outside kernel linear mapping)
- set_arch_dma_coherent_ops() implemented and bus notifiers removed
- EFI_STUB improvements when base of DRAM is occupied
- Typos in KGDB macros
- Clean-up to (partially) allow kernel building with LLVM
- Other clean-ups (extern keyword, phys_addr_t usage)
* tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: (51 commits)
arm64: Remove unneeded extern keyword
ARM64: make of_device_ids const
arm64: Use phys_addr_t type for physical address
aarch64: filter $x from kallsyms
arm64: Use DMA_ERROR_CODE to denote failed allocation
arm64: Fix typos in KGDB macros
arm64: insn: Add return statements after BUG_ON()
arm64: debug: don't re-enable debug exceptions on return from el1_dbg
Revert "arm64: dmi: Add SMBIOS/DMI support"
arm64: Implement set_arch_dma_coherent_ops() to replace bus notifiers
of: amba: use of_dma_configure for AMBA devices
arm64: dmi: Add SMBIOS/DMI support
arm64: Correct ftrace calls to aarch64_insn_gen_branch_imm()
arm64:mm: initialize max_mapnr using function set_max_mapnr
setup: Move unmask of async interrupts after possible earlycon setup
arm64: LLVMLinux: Fix inline arm64 assembly for use with clang
arm64: pageattr: Correctly adjust unaligned start addresses
net: bpf: arm64: fix module memory leak when JIT image build fails
arm64: add PSCI CPU_SUSPEND based cpu_suspend support
arm64: kernel: introduce cpu_init_idle CPU operation
...
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r-- | arch/arm64/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/cpuidle.c | 31 | ||||
-rw-r--r-- | arch/arm64/kernel/cpuinfo.c | 28 | ||||
-rw-r--r-- | arch/arm64/kernel/efi-stub.c | 16 | ||||
-rw-r--r-- | arch/arm64/kernel/entry.S | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/ftrace.c | 10 | ||||
-rw-r--r-- | arch/arm64/kernel/head.S | 6 | ||||
-rw-r--r-- | arch/arm64/kernel/insn.c | 671 | ||||
-rw-r--r-- | arch/arm64/kernel/kgdb.c | 4 | ||||
-rw-r--r-- | arch/arm64/kernel/perf_event.c | 2 | ||||
-rw-r--r-- | arch/arm64/kernel/process.c | 30 | ||||
-rw-r--r-- | arch/arm64/kernel/psci.c | 104 | ||||
-rw-r--r-- | arch/arm64/kernel/return_address.c | 3 | ||||
-rw-r--r-- | arch/arm64/kernel/setup.c | 11 | ||||
-rw-r--r-- | arch/arm64/kernel/sleep.S | 47 | ||||
-rw-r--r-- | arch/arm64/kernel/smp_spin_table.c | 22 | ||||
-rw-r--r-- | arch/arm64/kernel/stacktrace.c | 3 | ||||
-rw-r--r-- | arch/arm64/kernel/suspend.c | 48 | ||||
-rw-r--r-- | arch/arm64/kernel/traps.c | 3 |
19 files changed, 935 insertions, 106 deletions
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index df7ef8768fc2..6e9538c2d28a 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile | |||
@@ -26,6 +26,7 @@ arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o | |||
26 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o | 26 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o |
27 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o | 27 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o |
28 | arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o | 28 | arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o |
29 | arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o | ||
29 | arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o | 30 | arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o |
30 | arm64-obj-$(CONFIG_KGDB) += kgdb.o | 31 | arm64-obj-$(CONFIG_KGDB) += kgdb.o |
31 | arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o | 32 | arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o |
diff --git a/arch/arm64/kernel/cpuidle.c b/arch/arm64/kernel/cpuidle.c new file mode 100644 index 000000000000..19d17f51db37 --- /dev/null +++ b/arch/arm64/kernel/cpuidle.c | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * ARM64 CPU idle arch support | ||
3 | * | ||
4 | * Copyright (C) 2014 ARM Ltd. | ||
5 | * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/of.h> | ||
13 | #include <linux/of_device.h> | ||
14 | |||
15 | #include <asm/cpuidle.h> | ||
16 | #include <asm/cpu_ops.h> | ||
17 | |||
18 | int cpu_init_idle(unsigned int cpu) | ||
19 | { | ||
20 | int ret = -EOPNOTSUPP; | ||
21 | struct device_node *cpu_node = of_cpu_device_node_get(cpu); | ||
22 | |||
23 | if (!cpu_node) | ||
24 | return -ENODEV; | ||
25 | |||
26 | if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle) | ||
27 | ret = cpu_ops[cpu]->cpu_init_idle(cpu_node, cpu); | ||
28 | |||
29 | of_node_put(cpu_node); | ||
30 | return ret; | ||
31 | } | ||
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index 177169623026..504fdaa8367e 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c | |||
@@ -20,8 +20,10 @@ | |||
20 | #include <asm/cputype.h> | 20 | #include <asm/cputype.h> |
21 | 21 | ||
22 | #include <linux/bitops.h> | 22 | #include <linux/bitops.h> |
23 | #include <linux/bug.h> | ||
23 | #include <linux/init.h> | 24 | #include <linux/init.h> |
24 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
26 | #include <linux/preempt.h> | ||
25 | #include <linux/printk.h> | 27 | #include <linux/printk.h> |
26 | #include <linux/smp.h> | 28 | #include <linux/smp.h> |
27 | 29 | ||
@@ -47,8 +49,18 @@ static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info) | |||
47 | unsigned int cpu = smp_processor_id(); | 49 | unsigned int cpu = smp_processor_id(); |
48 | u32 l1ip = CTR_L1IP(info->reg_ctr); | 50 | u32 l1ip = CTR_L1IP(info->reg_ctr); |
49 | 51 | ||
50 | if (l1ip != ICACHE_POLICY_PIPT) | 52 | if (l1ip != ICACHE_POLICY_PIPT) { |
51 | set_bit(ICACHEF_ALIASING, &__icache_flags); | 53 | /* |
54 | * VIPT caches are non-aliasing if the VA always equals the PA | ||
55 | * in all bit positions that are covered by the index. This is | ||
56 | * the case if the size of a way (# of sets * line size) does | ||
57 | * not exceed PAGE_SIZE. | ||
58 | */ | ||
59 | u32 waysize = icache_get_numsets() * icache_get_linesize(); | ||
60 | |||
61 | if (l1ip != ICACHE_POLICY_VIPT || waysize > PAGE_SIZE) | ||
62 | set_bit(ICACHEF_ALIASING, &__icache_flags); | ||
63 | } | ||
52 | if (l1ip == ICACHE_POLICY_AIVIVT) | 64 | if (l1ip == ICACHE_POLICY_AIVIVT) |
53 | set_bit(ICACHEF_AIVIVT, &__icache_flags); | 65 | set_bit(ICACHEF_AIVIVT, &__icache_flags); |
54 | 66 | ||
@@ -190,3 +202,15 @@ void __init cpuinfo_store_boot_cpu(void) | |||
190 | 202 | ||
191 | boot_cpu_data = *info; | 203 | boot_cpu_data = *info; |
192 | } | 204 | } |
205 | |||
206 | u64 __attribute_const__ icache_get_ccsidr(void) | ||
207 | { | ||
208 | u64 ccsidr; | ||
209 | |||
210 | WARN_ON(preemptible()); | ||
211 | |||
212 | /* Select L1 I-cache and read its size ID register */ | ||
213 | asm("msr csselr_el1, %1; isb; mrs %0, ccsidr_el1" | ||
214 | : "=r"(ccsidr) : "r"(1L)); | ||
215 | return ccsidr; | ||
216 | } | ||
diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c index 1317fef8dde9..d27dd982ff26 100644 --- a/arch/arm64/kernel/efi-stub.c +++ b/arch/arm64/kernel/efi-stub.c | |||
@@ -28,20 +28,16 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, | |||
28 | kernel_size = _edata - _text; | 28 | kernel_size = _edata - _text; |
29 | if (*image_addr != (dram_base + TEXT_OFFSET)) { | 29 | if (*image_addr != (dram_base + TEXT_OFFSET)) { |
30 | kernel_memsize = kernel_size + (_end - _edata); | 30 | kernel_memsize = kernel_size + (_end - _edata); |
31 | status = efi_relocate_kernel(sys_table, image_addr, | 31 | status = efi_low_alloc(sys_table, kernel_memsize + TEXT_OFFSET, |
32 | kernel_size, kernel_memsize, | 32 | SZ_2M, reserve_addr); |
33 | dram_base + TEXT_OFFSET, | ||
34 | PAGE_SIZE); | ||
35 | if (status != EFI_SUCCESS) { | 33 | if (status != EFI_SUCCESS) { |
36 | pr_efi_err(sys_table, "Failed to relocate kernel\n"); | 34 | pr_efi_err(sys_table, "Failed to relocate kernel\n"); |
37 | return status; | 35 | return status; |
38 | } | 36 | } |
39 | if (*image_addr != (dram_base + TEXT_OFFSET)) { | 37 | memcpy((void *)*reserve_addr + TEXT_OFFSET, (void *)*image_addr, |
40 | pr_efi_err(sys_table, "Failed to alloc kernel memory\n"); | 38 | kernel_size); |
41 | efi_free(sys_table, kernel_memsize, *image_addr); | 39 | *image_addr = *reserve_addr + TEXT_OFFSET; |
42 | return EFI_LOAD_ERROR; | 40 | *reserve_size = kernel_memsize + TEXT_OFFSET; |
43 | } | ||
44 | *image_size = kernel_memsize; | ||
45 | } | 41 | } |
46 | 42 | ||
47 | 43 | ||
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index f0b5e5120a87..726b910fe6ec 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S | |||
@@ -324,7 +324,6 @@ el1_dbg: | |||
324 | mrs x0, far_el1 | 324 | mrs x0, far_el1 |
325 | mov x2, sp // struct pt_regs | 325 | mov x2, sp // struct pt_regs |
326 | bl do_debug_exception | 326 | bl do_debug_exception |
327 | enable_dbg | ||
328 | kernel_exit 1 | 327 | kernel_exit 1 |
329 | el1_inv: | 328 | el1_inv: |
330 | // TODO: add support for undefined instructions in kernel mode | 329 | // TODO: add support for undefined instructions in kernel mode |
diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c index 7924d73b6476..cf8556ae09d0 100644 --- a/arch/arm64/kernel/ftrace.c +++ b/arch/arm64/kernel/ftrace.c | |||
@@ -58,7 +58,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func) | |||
58 | u32 new; | 58 | u32 new; |
59 | 59 | ||
60 | pc = (unsigned long)&ftrace_call; | 60 | pc = (unsigned long)&ftrace_call; |
61 | new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); | 61 | new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, |
62 | AARCH64_INSN_BRANCH_LINK); | ||
62 | 63 | ||
63 | return ftrace_modify_code(pc, 0, new, false); | 64 | return ftrace_modify_code(pc, 0, new, false); |
64 | } | 65 | } |
@@ -72,7 +73,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) | |||
72 | u32 old, new; | 73 | u32 old, new; |
73 | 74 | ||
74 | old = aarch64_insn_gen_nop(); | 75 | old = aarch64_insn_gen_nop(); |
75 | new = aarch64_insn_gen_branch_imm(pc, addr, true); | 76 | new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK); |
76 | 77 | ||
77 | return ftrace_modify_code(pc, old, new, true); | 78 | return ftrace_modify_code(pc, old, new, true); |
78 | } | 79 | } |
@@ -86,7 +87,7 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, | |||
86 | unsigned long pc = rec->ip; | 87 | unsigned long pc = rec->ip; |
87 | u32 old, new; | 88 | u32 old, new; |
88 | 89 | ||
89 | old = aarch64_insn_gen_branch_imm(pc, addr, true); | 90 | old = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK); |
90 | new = aarch64_insn_gen_nop(); | 91 | new = aarch64_insn_gen_nop(); |
91 | 92 | ||
92 | return ftrace_modify_code(pc, old, new, true); | 93 | return ftrace_modify_code(pc, old, new, true); |
@@ -154,7 +155,8 @@ static int ftrace_modify_graph_caller(bool enable) | |||
154 | u32 branch, nop; | 155 | u32 branch, nop; |
155 | 156 | ||
156 | branch = aarch64_insn_gen_branch_imm(pc, | 157 | branch = aarch64_insn_gen_branch_imm(pc, |
157 | (unsigned long)ftrace_graph_caller, false); | 158 | (unsigned long)ftrace_graph_caller, |
159 | AARCH64_INSN_BRANCH_LINK); | ||
158 | nop = aarch64_insn_gen_nop(); | 160 | nop = aarch64_insn_gen_nop(); |
159 | 161 | ||
160 | if (enable) | 162 | if (enable) |
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 873069056229..0a6e4f924df8 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S | |||
@@ -151,7 +151,7 @@ optional_header: | |||
151 | .short 0x20b // PE32+ format | 151 | .short 0x20b // PE32+ format |
152 | .byte 0x02 // MajorLinkerVersion | 152 | .byte 0x02 // MajorLinkerVersion |
153 | .byte 0x14 // MinorLinkerVersion | 153 | .byte 0x14 // MinorLinkerVersion |
154 | .long _edata - stext // SizeOfCode | 154 | .long _end - stext // SizeOfCode |
155 | .long 0 // SizeOfInitializedData | 155 | .long 0 // SizeOfInitializedData |
156 | .long 0 // SizeOfUninitializedData | 156 | .long 0 // SizeOfUninitializedData |
157 | .long efi_stub_entry - efi_head // AddressOfEntryPoint | 157 | .long efi_stub_entry - efi_head // AddressOfEntryPoint |
@@ -169,7 +169,7 @@ extra_header_fields: | |||
169 | .short 0 // MinorSubsystemVersion | 169 | .short 0 // MinorSubsystemVersion |
170 | .long 0 // Win32VersionValue | 170 | .long 0 // Win32VersionValue |
171 | 171 | ||
172 | .long _edata - efi_head // SizeOfImage | 172 | .long _end - efi_head // SizeOfImage |
173 | 173 | ||
174 | // Everything before the kernel image is considered part of the header | 174 | // Everything before the kernel image is considered part of the header |
175 | .long stext - efi_head // SizeOfHeaders | 175 | .long stext - efi_head // SizeOfHeaders |
@@ -216,7 +216,7 @@ section_table: | |||
216 | .byte 0 | 216 | .byte 0 |
217 | .byte 0 | 217 | .byte 0 |
218 | .byte 0 // end of 0 padding of section name | 218 | .byte 0 // end of 0 padding of section name |
219 | .long _edata - stext // VirtualSize | 219 | .long _end - stext // VirtualSize |
220 | .long stext - efi_head // VirtualAddress | 220 | .long stext - efi_head // VirtualAddress |
221 | .long _edata - stext // SizeOfRawData | 221 | .long _edata - stext // SizeOfRawData |
222 | .long stext - efi_head // PointerToRawData | 222 | .long stext - efi_head // PointerToRawData |
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index 92f36835486b..e007714ded04 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c | |||
@@ -2,6 +2,8 @@ | |||
2 | * Copyright (C) 2013 Huawei Ltd. | 2 | * Copyright (C) 2013 Huawei Ltd. |
3 | * Author: Jiang Liu <liuj97@gmail.com> | 3 | * Author: Jiang Liu <liuj97@gmail.com> |
4 | * | 4 | * |
5 | * Copyright (C) 2014 Zi Shen Lim <zlim.lnx@gmail.com> | ||
6 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
@@ -20,9 +22,14 @@ | |||
20 | #include <linux/smp.h> | 22 | #include <linux/smp.h> |
21 | #include <linux/stop_machine.h> | 23 | #include <linux/stop_machine.h> |
22 | #include <linux/uaccess.h> | 24 | #include <linux/uaccess.h> |
25 | |||
23 | #include <asm/cacheflush.h> | 26 | #include <asm/cacheflush.h> |
27 | #include <asm/debug-monitors.h> | ||
24 | #include <asm/insn.h> | 28 | #include <asm/insn.h> |
25 | 29 | ||
30 | #define AARCH64_INSN_SF_BIT BIT(31) | ||
31 | #define AARCH64_INSN_N_BIT BIT(22) | ||
32 | |||
26 | static int aarch64_insn_encoding_class[] = { | 33 | static int aarch64_insn_encoding_class[] = { |
27 | AARCH64_INSN_CLS_UNKNOWN, | 34 | AARCH64_INSN_CLS_UNKNOWN, |
28 | AARCH64_INSN_CLS_UNKNOWN, | 35 | AARCH64_INSN_CLS_UNKNOWN, |
@@ -251,6 +258,19 @@ u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | |||
251 | mask = BIT(9) - 1; | 258 | mask = BIT(9) - 1; |
252 | shift = 12; | 259 | shift = 12; |
253 | break; | 260 | break; |
261 | case AARCH64_INSN_IMM_7: | ||
262 | mask = BIT(7) - 1; | ||
263 | shift = 15; | ||
264 | break; | ||
265 | case AARCH64_INSN_IMM_6: | ||
266 | case AARCH64_INSN_IMM_S: | ||
267 | mask = BIT(6) - 1; | ||
268 | shift = 10; | ||
269 | break; | ||
270 | case AARCH64_INSN_IMM_R: | ||
271 | mask = BIT(6) - 1; | ||
272 | shift = 16; | ||
273 | break; | ||
254 | default: | 274 | default: |
255 | pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n", | 275 | pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n", |
256 | type); | 276 | type); |
@@ -264,10 +284,76 @@ u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, | |||
264 | return insn; | 284 | return insn; |
265 | } | 285 | } |
266 | 286 | ||
267 | u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | 287 | static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type, |
268 | enum aarch64_insn_branch_type type) | 288 | u32 insn, |
289 | enum aarch64_insn_register reg) | ||
290 | { | ||
291 | int shift; | ||
292 | |||
293 | if (reg < AARCH64_INSN_REG_0 || reg > AARCH64_INSN_REG_SP) { | ||
294 | pr_err("%s: unknown register encoding %d\n", __func__, reg); | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | switch (type) { | ||
299 | case AARCH64_INSN_REGTYPE_RT: | ||
300 | case AARCH64_INSN_REGTYPE_RD: | ||
301 | shift = 0; | ||
302 | break; | ||
303 | case AARCH64_INSN_REGTYPE_RN: | ||
304 | shift = 5; | ||
305 | break; | ||
306 | case AARCH64_INSN_REGTYPE_RT2: | ||
307 | case AARCH64_INSN_REGTYPE_RA: | ||
308 | shift = 10; | ||
309 | break; | ||
310 | case AARCH64_INSN_REGTYPE_RM: | ||
311 | shift = 16; | ||
312 | break; | ||
313 | default: | ||
314 | pr_err("%s: unknown register type encoding %d\n", __func__, | ||
315 | type); | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | insn &= ~(GENMASK(4, 0) << shift); | ||
320 | insn |= reg << shift; | ||
321 | |||
322 | return insn; | ||
323 | } | ||
324 | |||
325 | static u32 aarch64_insn_encode_ldst_size(enum aarch64_insn_size_type type, | ||
326 | u32 insn) | ||
327 | { | ||
328 | u32 size; | ||
329 | |||
330 | switch (type) { | ||
331 | case AARCH64_INSN_SIZE_8: | ||
332 | size = 0; | ||
333 | break; | ||
334 | case AARCH64_INSN_SIZE_16: | ||
335 | size = 1; | ||
336 | break; | ||
337 | case AARCH64_INSN_SIZE_32: | ||
338 | size = 2; | ||
339 | break; | ||
340 | case AARCH64_INSN_SIZE_64: | ||
341 | size = 3; | ||
342 | break; | ||
343 | default: | ||
344 | pr_err("%s: unknown size encoding %d\n", __func__, type); | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | insn &= ~GENMASK(31, 30); | ||
349 | insn |= size << 30; | ||
350 | |||
351 | return insn; | ||
352 | } | ||
353 | |||
354 | static inline long branch_imm_common(unsigned long pc, unsigned long addr, | ||
355 | long range) | ||
269 | { | 356 | { |
270 | u32 insn; | ||
271 | long offset; | 357 | long offset; |
272 | 358 | ||
273 | /* | 359 | /* |
@@ -276,23 +362,97 @@ u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | |||
276 | */ | 362 | */ |
277 | BUG_ON((pc & 0x3) || (addr & 0x3)); | 363 | BUG_ON((pc & 0x3) || (addr & 0x3)); |
278 | 364 | ||
365 | offset = ((long)addr - (long)pc); | ||
366 | BUG_ON(offset < -range || offset >= range); | ||
367 | |||
368 | return offset; | ||
369 | } | ||
370 | |||
371 | u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, | ||
372 | enum aarch64_insn_branch_type type) | ||
373 | { | ||
374 | u32 insn; | ||
375 | long offset; | ||
376 | |||
279 | /* | 377 | /* |
280 | * B/BL support [-128M, 128M) offset | 378 | * B/BL support [-128M, 128M) offset |
281 | * ARM64 virtual address arrangement guarantees all kernel and module | 379 | * ARM64 virtual address arrangement guarantees all kernel and module |
282 | * texts are within +/-128M. | 380 | * texts are within +/-128M. |
283 | */ | 381 | */ |
284 | offset = ((long)addr - (long)pc); | 382 | offset = branch_imm_common(pc, addr, SZ_128M); |
285 | BUG_ON(offset < -SZ_128M || offset >= SZ_128M); | ||
286 | 383 | ||
287 | if (type == AARCH64_INSN_BRANCH_LINK) | 384 | switch (type) { |
385 | case AARCH64_INSN_BRANCH_LINK: | ||
288 | insn = aarch64_insn_get_bl_value(); | 386 | insn = aarch64_insn_get_bl_value(); |
289 | else | 387 | break; |
388 | case AARCH64_INSN_BRANCH_NOLINK: | ||
290 | insn = aarch64_insn_get_b_value(); | 389 | insn = aarch64_insn_get_b_value(); |
390 | break; | ||
391 | default: | ||
392 | BUG_ON(1); | ||
393 | return AARCH64_BREAK_FAULT; | ||
394 | } | ||
291 | 395 | ||
292 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn, | 396 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn, |
293 | offset >> 2); | 397 | offset >> 2); |
294 | } | 398 | } |
295 | 399 | ||
400 | u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr, | ||
401 | enum aarch64_insn_register reg, | ||
402 | enum aarch64_insn_variant variant, | ||
403 | enum aarch64_insn_branch_type type) | ||
404 | { | ||
405 | u32 insn; | ||
406 | long offset; | ||
407 | |||
408 | offset = branch_imm_common(pc, addr, SZ_1M); | ||
409 | |||
410 | switch (type) { | ||
411 | case AARCH64_INSN_BRANCH_COMP_ZERO: | ||
412 | insn = aarch64_insn_get_cbz_value(); | ||
413 | break; | ||
414 | case AARCH64_INSN_BRANCH_COMP_NONZERO: | ||
415 | insn = aarch64_insn_get_cbnz_value(); | ||
416 | break; | ||
417 | default: | ||
418 | BUG_ON(1); | ||
419 | return AARCH64_BREAK_FAULT; | ||
420 | } | ||
421 | |||
422 | switch (variant) { | ||
423 | case AARCH64_INSN_VARIANT_32BIT: | ||
424 | break; | ||
425 | case AARCH64_INSN_VARIANT_64BIT: | ||
426 | insn |= AARCH64_INSN_SF_BIT; | ||
427 | break; | ||
428 | default: | ||
429 | BUG_ON(1); | ||
430 | return AARCH64_BREAK_FAULT; | ||
431 | } | ||
432 | |||
433 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg); | ||
434 | |||
435 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn, | ||
436 | offset >> 2); | ||
437 | } | ||
438 | |||
439 | u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr, | ||
440 | enum aarch64_insn_condition cond) | ||
441 | { | ||
442 | u32 insn; | ||
443 | long offset; | ||
444 | |||
445 | offset = branch_imm_common(pc, addr, SZ_1M); | ||
446 | |||
447 | insn = aarch64_insn_get_bcond_value(); | ||
448 | |||
449 | BUG_ON(cond < AARCH64_INSN_COND_EQ || cond > AARCH64_INSN_COND_AL); | ||
450 | insn |= cond; | ||
451 | |||
452 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn, | ||
453 | offset >> 2); | ||
454 | } | ||
455 | |||
296 | u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op) | 456 | u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op) |
297 | { | 457 | { |
298 | return aarch64_insn_get_hint_value() | op; | 458 | return aarch64_insn_get_hint_value() | op; |
@@ -302,3 +462,500 @@ u32 __kprobes aarch64_insn_gen_nop(void) | |||
302 | { | 462 | { |
303 | return aarch64_insn_gen_hint(AARCH64_INSN_HINT_NOP); | 463 | return aarch64_insn_gen_hint(AARCH64_INSN_HINT_NOP); |
304 | } | 464 | } |
465 | |||
466 | u32 aarch64_insn_gen_branch_reg(enum aarch64_insn_register reg, | ||
467 | enum aarch64_insn_branch_type type) | ||
468 | { | ||
469 | u32 insn; | ||
470 | |||
471 | switch (type) { | ||
472 | case AARCH64_INSN_BRANCH_NOLINK: | ||
473 | insn = aarch64_insn_get_br_value(); | ||
474 | break; | ||
475 | case AARCH64_INSN_BRANCH_LINK: | ||
476 | insn = aarch64_insn_get_blr_value(); | ||
477 | break; | ||
478 | case AARCH64_INSN_BRANCH_RETURN: | ||
479 | insn = aarch64_insn_get_ret_value(); | ||
480 | break; | ||
481 | default: | ||
482 | BUG_ON(1); | ||
483 | return AARCH64_BREAK_FAULT; | ||
484 | } | ||
485 | |||
486 | return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, reg); | ||
487 | } | ||
488 | |||
489 | u32 aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg, | ||
490 | enum aarch64_insn_register base, | ||
491 | enum aarch64_insn_register offset, | ||
492 | enum aarch64_insn_size_type size, | ||
493 | enum aarch64_insn_ldst_type type) | ||
494 | { | ||
495 | u32 insn; | ||
496 | |||
497 | switch (type) { | ||
498 | case AARCH64_INSN_LDST_LOAD_REG_OFFSET: | ||
499 | insn = aarch64_insn_get_ldr_reg_value(); | ||
500 | break; | ||
501 | case AARCH64_INSN_LDST_STORE_REG_OFFSET: | ||
502 | insn = aarch64_insn_get_str_reg_value(); | ||
503 | break; | ||
504 | default: | ||
505 | BUG_ON(1); | ||
506 | return AARCH64_BREAK_FAULT; | ||
507 | } | ||
508 | |||
509 | insn = aarch64_insn_encode_ldst_size(size, insn); | ||
510 | |||
511 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg); | ||
512 | |||
513 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, | ||
514 | base); | ||
515 | |||
516 | return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, | ||
517 | offset); | ||
518 | } | ||
519 | |||
520 | u32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1, | ||
521 | enum aarch64_insn_register reg2, | ||
522 | enum aarch64_insn_register base, | ||
523 | int offset, | ||
524 | enum aarch64_insn_variant variant, | ||
525 | enum aarch64_insn_ldst_type type) | ||
526 | { | ||
527 | u32 insn; | ||
528 | int shift; | ||
529 | |||
530 | switch (type) { | ||
531 | case AARCH64_INSN_LDST_LOAD_PAIR_PRE_INDEX: | ||
532 | insn = aarch64_insn_get_ldp_pre_value(); | ||
533 | break; | ||
534 | case AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX: | ||
535 | insn = aarch64_insn_get_stp_pre_value(); | ||
536 | break; | ||
537 | case AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX: | ||
538 | insn = aarch64_insn_get_ldp_post_value(); | ||
539 | break; | ||
540 | case AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX: | ||
541 | insn = aarch64_insn_get_stp_post_value(); | ||
542 | break; | ||
543 | default: | ||
544 | BUG_ON(1); | ||
545 | return AARCH64_BREAK_FAULT; | ||
546 | } | ||
547 | |||
548 | switch (variant) { | ||
549 | case AARCH64_INSN_VARIANT_32BIT: | ||
550 | /* offset must be multiples of 4 in the range [-256, 252] */ | ||
551 | BUG_ON(offset & 0x3); | ||
552 | BUG_ON(offset < -256 || offset > 252); | ||
553 | shift = 2; | ||
554 | break; | ||
555 | case AARCH64_INSN_VARIANT_64BIT: | ||
556 | /* offset must be multiples of 8 in the range [-512, 504] */ | ||
557 | BUG_ON(offset & 0x7); | ||
558 | BUG_ON(offset < -512 || offset > 504); | ||
559 | shift = 3; | ||
560 | insn |= AARCH64_INSN_SF_BIT; | ||
561 | break; | ||
562 | default: | ||
563 | BUG_ON(1); | ||
564 | return AARCH64_BREAK_FAULT; | ||
565 | } | ||
566 | |||
567 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, | ||
568 | reg1); | ||
569 | |||
570 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT2, insn, | ||
571 | reg2); | ||
572 | |||
573 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, | ||
574 | base); | ||
575 | |||
576 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_7, insn, | ||
577 | offset >> shift); | ||
578 | } | ||
579 | |||
580 | u32 aarch64_insn_gen_add_sub_imm(enum aarch64_insn_register dst, | ||
581 | enum aarch64_insn_register src, | ||
582 | int imm, enum aarch64_insn_variant variant, | ||
583 | enum aarch64_insn_adsb_type type) | ||
584 | { | ||
585 | u32 insn; | ||
586 | |||
587 | switch (type) { | ||
588 | case AARCH64_INSN_ADSB_ADD: | ||
589 | insn = aarch64_insn_get_add_imm_value(); | ||
590 | break; | ||
591 | case AARCH64_INSN_ADSB_SUB: | ||
592 | insn = aarch64_insn_get_sub_imm_value(); | ||
593 | break; | ||
594 | case AARCH64_INSN_ADSB_ADD_SETFLAGS: | ||
595 | insn = aarch64_insn_get_adds_imm_value(); | ||
596 | break; | ||
597 | case AARCH64_INSN_ADSB_SUB_SETFLAGS: | ||
598 | insn = aarch64_insn_get_subs_imm_value(); | ||
599 | break; | ||
600 | default: | ||
601 | BUG_ON(1); | ||
602 | return AARCH64_BREAK_FAULT; | ||
603 | } | ||
604 | |||
605 | switch (variant) { | ||
606 | case AARCH64_INSN_VARIANT_32BIT: | ||
607 | break; | ||
608 | case AARCH64_INSN_VARIANT_64BIT: | ||
609 | insn |= AARCH64_INSN_SF_BIT; | ||
610 | break; | ||
611 | default: | ||
612 | BUG_ON(1); | ||
613 | return AARCH64_BREAK_FAULT; | ||
614 | } | ||
615 | |||
616 | BUG_ON(imm & ~(SZ_4K - 1)); | ||
617 | |||
618 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); | ||
619 | |||
620 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); | ||
621 | |||
622 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, imm); | ||
623 | } | ||
624 | |||
625 | u32 aarch64_insn_gen_bitfield(enum aarch64_insn_register dst, | ||
626 | enum aarch64_insn_register src, | ||
627 | int immr, int imms, | ||
628 | enum aarch64_insn_variant variant, | ||
629 | enum aarch64_insn_bitfield_type type) | ||
630 | { | ||
631 | u32 insn; | ||
632 | u32 mask; | ||
633 | |||
634 | switch (type) { | ||
635 | case AARCH64_INSN_BITFIELD_MOVE: | ||
636 | insn = aarch64_insn_get_bfm_value(); | ||
637 | break; | ||
638 | case AARCH64_INSN_BITFIELD_MOVE_UNSIGNED: | ||
639 | insn = aarch64_insn_get_ubfm_value(); | ||
640 | break; | ||
641 | case AARCH64_INSN_BITFIELD_MOVE_SIGNED: | ||
642 | insn = aarch64_insn_get_sbfm_value(); | ||
643 | break; | ||
644 | default: | ||
645 | BUG_ON(1); | ||
646 | return AARCH64_BREAK_FAULT; | ||
647 | } | ||
648 | |||
649 | switch (variant) { | ||
650 | case AARCH64_INSN_VARIANT_32BIT: | ||
651 | mask = GENMASK(4, 0); | ||
652 | break; | ||
653 | case AARCH64_INSN_VARIANT_64BIT: | ||
654 | insn |= AARCH64_INSN_SF_BIT | AARCH64_INSN_N_BIT; | ||
655 | mask = GENMASK(5, 0); | ||
656 | break; | ||
657 | default: | ||
658 | BUG_ON(1); | ||
659 | return AARCH64_BREAK_FAULT; | ||
660 | } | ||
661 | |||
662 | BUG_ON(immr & ~mask); | ||
663 | BUG_ON(imms & ~mask); | ||
664 | |||
665 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); | ||
666 | |||
667 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); | ||
668 | |||
669 | insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_R, insn, immr); | ||
670 | |||
671 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, imms); | ||
672 | } | ||
673 | |||
674 | u32 aarch64_insn_gen_movewide(enum aarch64_insn_register dst, | ||
675 | int imm, int shift, | ||
676 | enum aarch64_insn_variant variant, | ||
677 | enum aarch64_insn_movewide_type type) | ||
678 | { | ||
679 | u32 insn; | ||
680 | |||
681 | switch (type) { | ||
682 | case AARCH64_INSN_MOVEWIDE_ZERO: | ||
683 | insn = aarch64_insn_get_movz_value(); | ||
684 | break; | ||
685 | case AARCH64_INSN_MOVEWIDE_KEEP: | ||
686 | insn = aarch64_insn_get_movk_value(); | ||
687 | break; | ||
688 | case AARCH64_INSN_MOVEWIDE_INVERSE: | ||
689 | insn = aarch64_insn_get_movn_value(); | ||
690 | break; | ||
691 | default: | ||
692 | BUG_ON(1); | ||
693 | return AARCH64_BREAK_FAULT; | ||
694 | } | ||
695 | |||
696 | BUG_ON(imm & ~(SZ_64K - 1)); | ||
697 | |||
698 | switch (variant) { | ||
699 | case AARCH64_INSN_VARIANT_32BIT: | ||
700 | BUG_ON(shift != 0 && shift != 16); | ||
701 | break; | ||
702 | case AARCH64_INSN_VARIANT_64BIT: | ||
703 | insn |= AARCH64_INSN_SF_BIT; | ||
704 | BUG_ON(shift != 0 && shift != 16 && shift != 32 && | ||
705 | shift != 48); | ||
706 | break; | ||
707 | default: | ||
708 | BUG_ON(1); | ||
709 | return AARCH64_BREAK_FAULT; | ||
710 | } | ||
711 | |||
712 | insn |= (shift >> 4) << 21; | ||
713 | |||
714 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); | ||
715 | |||
716 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_16, insn, imm); | ||
717 | } | ||
718 | |||
719 | u32 aarch64_insn_gen_add_sub_shifted_reg(enum aarch64_insn_register dst, | ||
720 | enum aarch64_insn_register src, | ||
721 | enum aarch64_insn_register reg, | ||
722 | int shift, | ||
723 | enum aarch64_insn_variant variant, | ||
724 | enum aarch64_insn_adsb_type type) | ||
725 | { | ||
726 | u32 insn; | ||
727 | |||
728 | switch (type) { | ||
729 | case AARCH64_INSN_ADSB_ADD: | ||
730 | insn = aarch64_insn_get_add_value(); | ||
731 | break; | ||
732 | case AARCH64_INSN_ADSB_SUB: | ||
733 | insn = aarch64_insn_get_sub_value(); | ||
734 | break; | ||
735 | case AARCH64_INSN_ADSB_ADD_SETFLAGS: | ||
736 | insn = aarch64_insn_get_adds_value(); | ||
737 | break; | ||
738 | case AARCH64_INSN_ADSB_SUB_SETFLAGS: | ||
739 | insn = aarch64_insn_get_subs_value(); | ||
740 | break; | ||
741 | default: | ||
742 | BUG_ON(1); | ||
743 | return AARCH64_BREAK_FAULT; | ||
744 | } | ||
745 | |||
746 | switch (variant) { | ||
747 | case AARCH64_INSN_VARIANT_32BIT: | ||
748 | BUG_ON(shift & ~(SZ_32 - 1)); | ||
749 | break; | ||
750 | case AARCH64_INSN_VARIANT_64BIT: | ||
751 | insn |= AARCH64_INSN_SF_BIT; | ||
752 | BUG_ON(shift & ~(SZ_64 - 1)); | ||
753 | break; | ||
754 | default: | ||
755 | BUG_ON(1); | ||
756 | return AARCH64_BREAK_FAULT; | ||
757 | } | ||
758 | |||
759 | |||
760 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); | ||
761 | |||
762 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); | ||
763 | |||
764 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg); | ||
765 | |||
766 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift); | ||
767 | } | ||
768 | |||
769 | u32 aarch64_insn_gen_data1(enum aarch64_insn_register dst, | ||
770 | enum aarch64_insn_register src, | ||
771 | enum aarch64_insn_variant variant, | ||
772 | enum aarch64_insn_data1_type type) | ||
773 | { | ||
774 | u32 insn; | ||
775 | |||
776 | switch (type) { | ||
777 | case AARCH64_INSN_DATA1_REVERSE_16: | ||
778 | insn = aarch64_insn_get_rev16_value(); | ||
779 | break; | ||
780 | case AARCH64_INSN_DATA1_REVERSE_32: | ||
781 | insn = aarch64_insn_get_rev32_value(); | ||
782 | break; | ||
783 | case AARCH64_INSN_DATA1_REVERSE_64: | ||
784 | BUG_ON(variant != AARCH64_INSN_VARIANT_64BIT); | ||
785 | insn = aarch64_insn_get_rev64_value(); | ||
786 | break; | ||
787 | default: | ||
788 | BUG_ON(1); | ||
789 | return AARCH64_BREAK_FAULT; | ||
790 | } | ||
791 | |||
792 | switch (variant) { | ||
793 | case AARCH64_INSN_VARIANT_32BIT: | ||
794 | break; | ||
795 | case AARCH64_INSN_VARIANT_64BIT: | ||
796 | insn |= AARCH64_INSN_SF_BIT; | ||
797 | break; | ||
798 | default: | ||
799 | BUG_ON(1); | ||
800 | return AARCH64_BREAK_FAULT; | ||
801 | } | ||
802 | |||
803 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); | ||
804 | |||
805 | return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); | ||
806 | } | ||
807 | |||
808 | u32 aarch64_insn_gen_data2(enum aarch64_insn_register dst, | ||
809 | enum aarch64_insn_register src, | ||
810 | enum aarch64_insn_register reg, | ||
811 | enum aarch64_insn_variant variant, | ||
812 | enum aarch64_insn_data2_type type) | ||
813 | { | ||
814 | u32 insn; | ||
815 | |||
816 | switch (type) { | ||
817 | case AARCH64_INSN_DATA2_UDIV: | ||
818 | insn = aarch64_insn_get_udiv_value(); | ||
819 | break; | ||
820 | case AARCH64_INSN_DATA2_SDIV: | ||
821 | insn = aarch64_insn_get_sdiv_value(); | ||
822 | break; | ||
823 | case AARCH64_INSN_DATA2_LSLV: | ||
824 | insn = aarch64_insn_get_lslv_value(); | ||
825 | break; | ||
826 | case AARCH64_INSN_DATA2_LSRV: | ||
827 | insn = aarch64_insn_get_lsrv_value(); | ||
828 | break; | ||
829 | case AARCH64_INSN_DATA2_ASRV: | ||
830 | insn = aarch64_insn_get_asrv_value(); | ||
831 | break; | ||
832 | case AARCH64_INSN_DATA2_RORV: | ||
833 | insn = aarch64_insn_get_rorv_value(); | ||
834 | break; | ||
835 | default: | ||
836 | BUG_ON(1); | ||
837 | return AARCH64_BREAK_FAULT; | ||
838 | } | ||
839 | |||
840 | switch (variant) { | ||
841 | case AARCH64_INSN_VARIANT_32BIT: | ||
842 | break; | ||
843 | case AARCH64_INSN_VARIANT_64BIT: | ||
844 | insn |= AARCH64_INSN_SF_BIT; | ||
845 | break; | ||
846 | default: | ||
847 | BUG_ON(1); | ||
848 | return AARCH64_BREAK_FAULT; | ||
849 | } | ||
850 | |||
851 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); | ||
852 | |||
853 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); | ||
854 | |||
855 | return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg); | ||
856 | } | ||
857 | |||
858 | u32 aarch64_insn_gen_data3(enum aarch64_insn_register dst, | ||
859 | enum aarch64_insn_register src, | ||
860 | enum aarch64_insn_register reg1, | ||
861 | enum aarch64_insn_register reg2, | ||
862 | enum aarch64_insn_variant variant, | ||
863 | enum aarch64_insn_data3_type type) | ||
864 | { | ||
865 | u32 insn; | ||
866 | |||
867 | switch (type) { | ||
868 | case AARCH64_INSN_DATA3_MADD: | ||
869 | insn = aarch64_insn_get_madd_value(); | ||
870 | break; | ||
871 | case AARCH64_INSN_DATA3_MSUB: | ||
872 | insn = aarch64_insn_get_msub_value(); | ||
873 | break; | ||
874 | default: | ||
875 | BUG_ON(1); | ||
876 | return AARCH64_BREAK_FAULT; | ||
877 | } | ||
878 | |||
879 | switch (variant) { | ||
880 | case AARCH64_INSN_VARIANT_32BIT: | ||
881 | break; | ||
882 | case AARCH64_INSN_VARIANT_64BIT: | ||
883 | insn |= AARCH64_INSN_SF_BIT; | ||
884 | break; | ||
885 | default: | ||
886 | BUG_ON(1); | ||
887 | return AARCH64_BREAK_FAULT; | ||
888 | } | ||
889 | |||
890 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); | ||
891 | |||
892 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RA, insn, src); | ||
893 | |||
894 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, | ||
895 | reg1); | ||
896 | |||
897 | return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, | ||
898 | reg2); | ||
899 | } | ||
900 | |||
901 | u32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst, | ||
902 | enum aarch64_insn_register src, | ||
903 | enum aarch64_insn_register reg, | ||
904 | int shift, | ||
905 | enum aarch64_insn_variant variant, | ||
906 | enum aarch64_insn_logic_type type) | ||
907 | { | ||
908 | u32 insn; | ||
909 | |||
910 | switch (type) { | ||
911 | case AARCH64_INSN_LOGIC_AND: | ||
912 | insn = aarch64_insn_get_and_value(); | ||
913 | break; | ||
914 | case AARCH64_INSN_LOGIC_BIC: | ||
915 | insn = aarch64_insn_get_bic_value(); | ||
916 | break; | ||
917 | case AARCH64_INSN_LOGIC_ORR: | ||
918 | insn = aarch64_insn_get_orr_value(); | ||
919 | break; | ||
920 | case AARCH64_INSN_LOGIC_ORN: | ||
921 | insn = aarch64_insn_get_orn_value(); | ||
922 | break; | ||
923 | case AARCH64_INSN_LOGIC_EOR: | ||
924 | insn = aarch64_insn_get_eor_value(); | ||
925 | break; | ||
926 | case AARCH64_INSN_LOGIC_EON: | ||
927 | insn = aarch64_insn_get_eon_value(); | ||
928 | break; | ||
929 | case AARCH64_INSN_LOGIC_AND_SETFLAGS: | ||
930 | insn = aarch64_insn_get_ands_value(); | ||
931 | break; | ||
932 | case AARCH64_INSN_LOGIC_BIC_SETFLAGS: | ||
933 | insn = aarch64_insn_get_bics_value(); | ||
934 | break; | ||
935 | default: | ||
936 | BUG_ON(1); | ||
937 | return AARCH64_BREAK_FAULT; | ||
938 | } | ||
939 | |||
940 | switch (variant) { | ||
941 | case AARCH64_INSN_VARIANT_32BIT: | ||
942 | BUG_ON(shift & ~(SZ_32 - 1)); | ||
943 | break; | ||
944 | case AARCH64_INSN_VARIANT_64BIT: | ||
945 | insn |= AARCH64_INSN_SF_BIT; | ||
946 | BUG_ON(shift & ~(SZ_64 - 1)); | ||
947 | break; | ||
948 | default: | ||
949 | BUG_ON(1); | ||
950 | return AARCH64_BREAK_FAULT; | ||
951 | } | ||
952 | |||
953 | |||
954 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); | ||
955 | |||
956 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); | ||
957 | |||
958 | insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg); | ||
959 | |||
960 | return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift); | ||
961 | } | ||
diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c index 75c9cf1aafee..a0d10c55f307 100644 --- a/arch/arm64/kernel/kgdb.c +++ b/arch/arm64/kernel/kgdb.c | |||
@@ -235,13 +235,13 @@ static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr) | |||
235 | 235 | ||
236 | static struct break_hook kgdb_brkpt_hook = { | 236 | static struct break_hook kgdb_brkpt_hook = { |
237 | .esr_mask = 0xffffffff, | 237 | .esr_mask = 0xffffffff, |
238 | .esr_val = DBG_ESR_VAL_BRK(KGDB_DYN_DGB_BRK_IMM), | 238 | .esr_val = DBG_ESR_VAL_BRK(KGDB_DYN_DBG_BRK_IMM), |
239 | .fn = kgdb_brk_fn | 239 | .fn = kgdb_brk_fn |
240 | }; | 240 | }; |
241 | 241 | ||
242 | static struct break_hook kgdb_compiled_brkpt_hook = { | 242 | static struct break_hook kgdb_compiled_brkpt_hook = { |
243 | .esr_mask = 0xffffffff, | 243 | .esr_mask = 0xffffffff, |
244 | .esr_val = DBG_ESR_VAL_BRK(KDBG_COMPILED_DBG_BRK_IMM), | 244 | .esr_val = DBG_ESR_VAL_BRK(KGDB_COMPILED_DBG_BRK_IMM), |
245 | .fn = kgdb_compiled_brk_fn | 245 | .fn = kgdb_compiled_brk_fn |
246 | }; | 246 | }; |
247 | 247 | ||
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index baf5afb7e6a0..aa29ecb4f800 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c | |||
@@ -1276,7 +1276,7 @@ arch_initcall(cpu_pmu_reset); | |||
1276 | /* | 1276 | /* |
1277 | * PMU platform driver and devicetree bindings. | 1277 | * PMU platform driver and devicetree bindings. |
1278 | */ | 1278 | */ |
1279 | static struct of_device_id armpmu_of_device_ids[] = { | 1279 | static const struct of_device_id armpmu_of_device_ids[] = { |
1280 | {.compatible = "arm,armv8-pmuv3"}, | 1280 | {.compatible = "arm,armv8-pmuv3"}, |
1281 | {}, | 1281 | {}, |
1282 | }; | 1282 | }; |
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 29d48690f2ac..89f41f7d27dd 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c | |||
@@ -57,36 +57,10 @@ unsigned long __stack_chk_guard __read_mostly; | |||
57 | EXPORT_SYMBOL(__stack_chk_guard); | 57 | EXPORT_SYMBOL(__stack_chk_guard); |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | static void setup_restart(void) | ||
61 | { | ||
62 | /* | ||
63 | * Tell the mm system that we are going to reboot - | ||
64 | * we may need it to insert some 1:1 mappings so that | ||
65 | * soft boot works. | ||
66 | */ | ||
67 | setup_mm_for_reboot(); | ||
68 | |||
69 | /* Clean and invalidate caches */ | ||
70 | flush_cache_all(); | ||
71 | |||
72 | /* Turn D-cache off */ | ||
73 | cpu_cache_off(); | ||
74 | |||
75 | /* Push out any further dirty data, and ensure cache is empty */ | ||
76 | flush_cache_all(); | ||
77 | } | ||
78 | |||
79 | void soft_restart(unsigned long addr) | 60 | void soft_restart(unsigned long addr) |
80 | { | 61 | { |
81 | typedef void (*phys_reset_t)(unsigned long); | 62 | setup_mm_for_reboot(); |
82 | phys_reset_t phys_reset; | 63 | cpu_soft_restart(virt_to_phys(cpu_reset), addr); |
83 | |||
84 | setup_restart(); | ||
85 | |||
86 | /* Switch to the identity mapping */ | ||
87 | phys_reset = (phys_reset_t)virt_to_phys(cpu_reset); | ||
88 | phys_reset(addr); | ||
89 | |||
90 | /* Should never get here */ | 64 | /* Should never get here */ |
91 | BUG(); | 65 | BUG(); |
92 | } | 66 | } |
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 553954771a67..866c1c821860 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/reboot.h> | 21 | #include <linux/reboot.h> |
22 | #include <linux/pm.h> | 22 | #include <linux/pm.h> |
23 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
24 | #include <linux/slab.h> | ||
24 | #include <uapi/linux/psci.h> | 25 | #include <uapi/linux/psci.h> |
25 | 26 | ||
26 | #include <asm/compiler.h> | 27 | #include <asm/compiler.h> |
@@ -28,6 +29,7 @@ | |||
28 | #include <asm/errno.h> | 29 | #include <asm/errno.h> |
29 | #include <asm/psci.h> | 30 | #include <asm/psci.h> |
30 | #include <asm/smp_plat.h> | 31 | #include <asm/smp_plat.h> |
32 | #include <asm/suspend.h> | ||
31 | #include <asm/system_misc.h> | 33 | #include <asm/system_misc.h> |
32 | 34 | ||
33 | #define PSCI_POWER_STATE_TYPE_STANDBY 0 | 35 | #define PSCI_POWER_STATE_TYPE_STANDBY 0 |
@@ -65,6 +67,8 @@ enum psci_function { | |||
65 | PSCI_FN_MAX, | 67 | PSCI_FN_MAX, |
66 | }; | 68 | }; |
67 | 69 | ||
70 | static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state); | ||
71 | |||
68 | static u32 psci_function_id[PSCI_FN_MAX]; | 72 | static u32 psci_function_id[PSCI_FN_MAX]; |
69 | 73 | ||
70 | static int psci_to_linux_errno(int errno) | 74 | static int psci_to_linux_errno(int errno) |
@@ -93,6 +97,18 @@ static u32 psci_power_state_pack(struct psci_power_state state) | |||
93 | & PSCI_0_2_POWER_STATE_AFFL_MASK); | 97 | & PSCI_0_2_POWER_STATE_AFFL_MASK); |
94 | } | 98 | } |
95 | 99 | ||
100 | static void psci_power_state_unpack(u32 power_state, | ||
101 | struct psci_power_state *state) | ||
102 | { | ||
103 | state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >> | ||
104 | PSCI_0_2_POWER_STATE_ID_SHIFT; | ||
105 | state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >> | ||
106 | PSCI_0_2_POWER_STATE_TYPE_SHIFT; | ||
107 | state->affinity_level = | ||
108 | (power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >> | ||
109 | PSCI_0_2_POWER_STATE_AFFL_SHIFT; | ||
110 | } | ||
111 | |||
96 | /* | 112 | /* |
97 | * The following two functions are invoked via the invoke_psci_fn pointer | 113 | * The following two functions are invoked via the invoke_psci_fn pointer |
98 | * and will not be inlined, allowing us to piggyback on the AAPCS. | 114 | * and will not be inlined, allowing us to piggyback on the AAPCS. |
@@ -199,6 +215,63 @@ static int psci_migrate_info_type(void) | |||
199 | return err; | 215 | return err; |
200 | } | 216 | } |
201 | 217 | ||
218 | static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_node, | ||
219 | unsigned int cpu) | ||
220 | { | ||
221 | int i, ret, count = 0; | ||
222 | struct psci_power_state *psci_states; | ||
223 | struct device_node *state_node; | ||
224 | |||
225 | /* | ||
226 | * If the PSCI cpu_suspend function hook has not been initialized | ||
227 | * idle states must not be enabled, so bail out | ||
228 | */ | ||
229 | if (!psci_ops.cpu_suspend) | ||
230 | return -EOPNOTSUPP; | ||
231 | |||
232 | /* Count idle states */ | ||
233 | while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", | ||
234 | count))) { | ||
235 | count++; | ||
236 | of_node_put(state_node); | ||
237 | } | ||
238 | |||
239 | if (!count) | ||
240 | return -ENODEV; | ||
241 | |||
242 | psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); | ||
243 | if (!psci_states) | ||
244 | return -ENOMEM; | ||
245 | |||
246 | for (i = 0; i < count; i++) { | ||
247 | u32 psci_power_state; | ||
248 | |||
249 | state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); | ||
250 | |||
251 | ret = of_property_read_u32(state_node, | ||
252 | "arm,psci-suspend-param", | ||
253 | &psci_power_state); | ||
254 | if (ret) { | ||
255 | pr_warn(" * %s missing arm,psci-suspend-param property\n", | ||
256 | state_node->full_name); | ||
257 | of_node_put(state_node); | ||
258 | goto free_mem; | ||
259 | } | ||
260 | |||
261 | of_node_put(state_node); | ||
262 | pr_debug("psci-power-state %#x index %d\n", psci_power_state, | ||
263 | i); | ||
264 | psci_power_state_unpack(psci_power_state, &psci_states[i]); | ||
265 | } | ||
266 | /* Idle states parsed correctly, initialize per-cpu pointer */ | ||
267 | per_cpu(psci_power_state, cpu) = psci_states; | ||
268 | return 0; | ||
269 | |||
270 | free_mem: | ||
271 | kfree(psci_states); | ||
272 | return ret; | ||
273 | } | ||
274 | |||
202 | static int get_set_conduit_method(struct device_node *np) | 275 | static int get_set_conduit_method(struct device_node *np) |
203 | { | 276 | { |
204 | const char *method; | 277 | const char *method; |
@@ -436,8 +509,39 @@ static int cpu_psci_cpu_kill(unsigned int cpu) | |||
436 | #endif | 509 | #endif |
437 | #endif | 510 | #endif |
438 | 511 | ||
512 | static int psci_suspend_finisher(unsigned long index) | ||
513 | { | ||
514 | struct psci_power_state *state = __get_cpu_var(psci_power_state); | ||
515 | |||
516 | return psci_ops.cpu_suspend(state[index - 1], | ||
517 | virt_to_phys(cpu_resume)); | ||
518 | } | ||
519 | |||
520 | static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index) | ||
521 | { | ||
522 | int ret; | ||
523 | struct psci_power_state *state = __get_cpu_var(psci_power_state); | ||
524 | /* | ||
525 | * idle state index 0 corresponds to wfi, should never be called | ||
526 | * from the cpu_suspend operations | ||
527 | */ | ||
528 | if (WARN_ON_ONCE(!index)) | ||
529 | return -EINVAL; | ||
530 | |||
531 | if (state->type == PSCI_POWER_STATE_TYPE_STANDBY) | ||
532 | ret = psci_ops.cpu_suspend(state[index - 1], 0); | ||
533 | else | ||
534 | ret = __cpu_suspend(index, psci_suspend_finisher); | ||
535 | |||
536 | return ret; | ||
537 | } | ||
538 | |||
439 | const struct cpu_operations cpu_psci_ops = { | 539 | const struct cpu_operations cpu_psci_ops = { |
440 | .name = "psci", | 540 | .name = "psci", |
541 | #ifdef CONFIG_CPU_IDLE | ||
542 | .cpu_init_idle = cpu_psci_cpu_init_idle, | ||
543 | .cpu_suspend = cpu_psci_cpu_suspend, | ||
544 | #endif | ||
441 | #ifdef CONFIG_SMP | 545 | #ifdef CONFIG_SMP |
442 | .cpu_init = cpu_psci_cpu_init, | 546 | .cpu_init = cpu_psci_cpu_init, |
443 | .cpu_prepare = cpu_psci_cpu_prepare, | 547 | .cpu_prepare = cpu_psci_cpu_prepare, |
diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c index 89102a6ffad5..6c4fd2810ecb 100644 --- a/arch/arm64/kernel/return_address.c +++ b/arch/arm64/kernel/return_address.c | |||
@@ -36,13 +36,12 @@ void *return_address(unsigned int level) | |||
36 | { | 36 | { |
37 | struct return_address_data data; | 37 | struct return_address_data data; |
38 | struct stackframe frame; | 38 | struct stackframe frame; |
39 | register unsigned long current_sp asm ("sp"); | ||
40 | 39 | ||
41 | data.level = level + 2; | 40 | data.level = level + 2; |
42 | data.addr = NULL; | 41 | data.addr = NULL; |
43 | 42 | ||
44 | frame.fp = (unsigned long)__builtin_frame_address(0); | 43 | frame.fp = (unsigned long)__builtin_frame_address(0); |
45 | frame.sp = current_sp; | 44 | frame.sp = current_stack_pointer; |
46 | frame.pc = (unsigned long)return_address; /* dummy */ | 45 | frame.pc = (unsigned long)return_address; /* dummy */ |
47 | 46 | ||
48 | walk_stackframe(&frame, save_return_addr, &data); | 47 | walk_stackframe(&frame, save_return_addr, &data); |
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index edb146d01857..2437196cc5d4 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c | |||
@@ -365,11 +365,6 @@ u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID }; | |||
365 | 365 | ||
366 | void __init setup_arch(char **cmdline_p) | 366 | void __init setup_arch(char **cmdline_p) |
367 | { | 367 | { |
368 | /* | ||
369 | * Unmask asynchronous aborts early to catch possible system errors. | ||
370 | */ | ||
371 | local_async_enable(); | ||
372 | |||
373 | setup_processor(); | 368 | setup_processor(); |
374 | 369 | ||
375 | setup_machine_fdt(__fdt_pointer); | 370 | setup_machine_fdt(__fdt_pointer); |
@@ -385,6 +380,12 @@ void __init setup_arch(char **cmdline_p) | |||
385 | 380 | ||
386 | parse_early_param(); | 381 | parse_early_param(); |
387 | 382 | ||
383 | /* | ||
384 | * Unmask asynchronous aborts after bringing up possible earlycon. | ||
385 | * (Report possible System Errors once we can report this occurred) | ||
386 | */ | ||
387 | local_async_enable(); | ||
388 | |||
388 | efi_init(); | 389 | efi_init(); |
389 | arm64_memblock_init(); | 390 | arm64_memblock_init(); |
390 | 391 | ||
diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S index b1925729c692..a564b440416a 100644 --- a/arch/arm64/kernel/sleep.S +++ b/arch/arm64/kernel/sleep.S | |||
@@ -49,28 +49,39 @@ | |||
49 | orr \dst, \dst, \mask // dst|=(aff3>>rs3) | 49 | orr \dst, \dst, \mask // dst|=(aff3>>rs3) |
50 | .endm | 50 | .endm |
51 | /* | 51 | /* |
52 | * Save CPU state for a suspend. This saves callee registers, and allocates | 52 | * Save CPU state for a suspend and execute the suspend finisher. |
53 | * space on the kernel stack to save the CPU specific registers + some | 53 | * On success it will return 0 through cpu_resume - ie through a CPU |
54 | * other data for resume. | 54 | * soft/hard reboot from the reset vector. |
55 | * On failure it returns the suspend finisher return value or force | ||
56 | * -EOPNOTSUPP if the finisher erroneously returns 0 (the suspend finisher | ||
57 | * is not allowed to return, if it does this must be considered failure). | ||
58 | * It saves callee registers, and allocates space on the kernel stack | ||
59 | * to save the CPU specific registers + some other data for resume. | ||
55 | * | 60 | * |
56 | * x0 = suspend finisher argument | 61 | * x0 = suspend finisher argument |
62 | * x1 = suspend finisher function pointer | ||
57 | */ | 63 | */ |
58 | ENTRY(__cpu_suspend) | 64 | ENTRY(__cpu_suspend_enter) |
59 | stp x29, lr, [sp, #-96]! | 65 | stp x29, lr, [sp, #-96]! |
60 | stp x19, x20, [sp,#16] | 66 | stp x19, x20, [sp,#16] |
61 | stp x21, x22, [sp,#32] | 67 | stp x21, x22, [sp,#32] |
62 | stp x23, x24, [sp,#48] | 68 | stp x23, x24, [sp,#48] |
63 | stp x25, x26, [sp,#64] | 69 | stp x25, x26, [sp,#64] |
64 | stp x27, x28, [sp,#80] | 70 | stp x27, x28, [sp,#80] |
71 | /* | ||
72 | * Stash suspend finisher and its argument in x20 and x19 | ||
73 | */ | ||
74 | mov x19, x0 | ||
75 | mov x20, x1 | ||
65 | mov x2, sp | 76 | mov x2, sp |
66 | sub sp, sp, #CPU_SUSPEND_SZ // allocate cpu_suspend_ctx | 77 | sub sp, sp, #CPU_SUSPEND_SZ // allocate cpu_suspend_ctx |
67 | mov x1, sp | 78 | mov x0, sp |
68 | /* | 79 | /* |
69 | * x1 now points to struct cpu_suspend_ctx allocated on the stack | 80 | * x0 now points to struct cpu_suspend_ctx allocated on the stack |
70 | */ | 81 | */ |
71 | str x2, [x1, #CPU_CTX_SP] | 82 | str x2, [x0, #CPU_CTX_SP] |
72 | ldr x2, =sleep_save_sp | 83 | ldr x1, =sleep_save_sp |
73 | ldr x2, [x2, #SLEEP_SAVE_SP_VIRT] | 84 | ldr x1, [x1, #SLEEP_SAVE_SP_VIRT] |
74 | #ifdef CONFIG_SMP | 85 | #ifdef CONFIG_SMP |
75 | mrs x7, mpidr_el1 | 86 | mrs x7, mpidr_el1 |
76 | ldr x9, =mpidr_hash | 87 | ldr x9, =mpidr_hash |
@@ -82,11 +93,21 @@ ENTRY(__cpu_suspend) | |||
82 | ldp w3, w4, [x9, #MPIDR_HASH_SHIFTS] | 93 | ldp w3, w4, [x9, #MPIDR_HASH_SHIFTS] |
83 | ldp w5, w6, [x9, #(MPIDR_HASH_SHIFTS + 8)] | 94 | ldp w5, w6, [x9, #(MPIDR_HASH_SHIFTS + 8)] |
84 | compute_mpidr_hash x8, x3, x4, x5, x6, x7, x10 | 95 | compute_mpidr_hash x8, x3, x4, x5, x6, x7, x10 |
85 | add x2, x2, x8, lsl #3 | 96 | add x1, x1, x8, lsl #3 |
86 | #endif | 97 | #endif |
87 | bl __cpu_suspend_finisher | 98 | bl __cpu_suspend_save |
99 | /* | ||
100 | * Grab suspend finisher in x20 and its argument in x19 | ||
101 | */ | ||
102 | mov x0, x19 | ||
103 | mov x1, x20 | ||
104 | /* | ||
105 | * We are ready for power down, fire off the suspend finisher | ||
106 | * in x1, with argument in x0 | ||
107 | */ | ||
108 | blr x1 | ||
88 | /* | 109 | /* |
89 | * Never gets here, unless suspend fails. | 110 | * Never gets here, unless suspend finisher fails. |
90 | * Successful cpu_suspend should return from cpu_resume, returning | 111 | * Successful cpu_suspend should return from cpu_resume, returning |
91 | * through this code path is considered an error | 112 | * through this code path is considered an error |
92 | * If the return value is set to 0 force x0 = -EOPNOTSUPP | 113 | * If the return value is set to 0 force x0 = -EOPNOTSUPP |
@@ -103,7 +124,7 @@ ENTRY(__cpu_suspend) | |||
103 | ldp x27, x28, [sp, #80] | 124 | ldp x27, x28, [sp, #80] |
104 | ldp x29, lr, [sp], #96 | 125 | ldp x29, lr, [sp], #96 |
105 | ret | 126 | ret |
106 | ENDPROC(__cpu_suspend) | 127 | ENDPROC(__cpu_suspend_enter) |
107 | .ltorg | 128 | .ltorg |
108 | 129 | ||
109 | /* | 130 | /* |
diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c index 0347d38eea29..4f93c67e63de 100644 --- a/arch/arm64/kernel/smp_spin_table.c +++ b/arch/arm64/kernel/smp_spin_table.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
21 | #include <linux/of.h> | 21 | #include <linux/of.h> |
22 | #include <linux/smp.h> | 22 | #include <linux/smp.h> |
23 | #include <linux/types.h> | ||
23 | 24 | ||
24 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
25 | #include <asm/cpu_ops.h> | 26 | #include <asm/cpu_ops.h> |
@@ -65,12 +66,21 @@ static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu) | |||
65 | 66 | ||
66 | static int smp_spin_table_cpu_prepare(unsigned int cpu) | 67 | static int smp_spin_table_cpu_prepare(unsigned int cpu) |
67 | { | 68 | { |
68 | void **release_addr; | 69 | __le64 __iomem *release_addr; |
69 | 70 | ||
70 | if (!cpu_release_addr[cpu]) | 71 | if (!cpu_release_addr[cpu]) |
71 | return -ENODEV; | 72 | return -ENODEV; |
72 | 73 | ||
73 | release_addr = __va(cpu_release_addr[cpu]); | 74 | /* |
75 | * The cpu-release-addr may or may not be inside the linear mapping. | ||
76 | * As ioremap_cache will either give us a new mapping or reuse the | ||
77 | * existing linear mapping, we can use it to cover both cases. In | ||
78 | * either case the memory will be MT_NORMAL. | ||
79 | */ | ||
80 | release_addr = ioremap_cache(cpu_release_addr[cpu], | ||
81 | sizeof(*release_addr)); | ||
82 | if (!release_addr) | ||
83 | return -ENOMEM; | ||
74 | 84 | ||
75 | /* | 85 | /* |
76 | * We write the release address as LE regardless of the native | 86 | * We write the release address as LE regardless of the native |
@@ -79,15 +89,17 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu) | |||
79 | * boot-loader's endianess before jumping. This is mandated by | 89 | * boot-loader's endianess before jumping. This is mandated by |
80 | * the boot protocol. | 90 | * the boot protocol. |
81 | */ | 91 | */ |
82 | release_addr[0] = (void *) cpu_to_le64(__pa(secondary_holding_pen)); | 92 | writeq_relaxed(__pa(secondary_holding_pen), release_addr); |
83 | 93 | __flush_dcache_area((__force void *)release_addr, | |
84 | __flush_dcache_area(release_addr, sizeof(release_addr[0])); | 94 | sizeof(*release_addr)); |
85 | 95 | ||
86 | /* | 96 | /* |
87 | * Send an event to wake up the secondary CPU. | 97 | * Send an event to wake up the secondary CPU. |
88 | */ | 98 | */ |
89 | sev(); | 99 | sev(); |
90 | 100 | ||
101 | iounmap(release_addr); | ||
102 | |||
91 | return 0; | 103 | return 0; |
92 | } | 104 | } |
93 | 105 | ||
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index 55437ba1f5a4..407991bf79f5 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c | |||
@@ -111,10 +111,9 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |||
111 | frame.sp = thread_saved_sp(tsk); | 111 | frame.sp = thread_saved_sp(tsk); |
112 | frame.pc = thread_saved_pc(tsk); | 112 | frame.pc = thread_saved_pc(tsk); |
113 | } else { | 113 | } else { |
114 | register unsigned long current_sp asm("sp"); | ||
115 | data.no_sched_functions = 0; | 114 | data.no_sched_functions = 0; |
116 | frame.fp = (unsigned long)__builtin_frame_address(0); | 115 | frame.fp = (unsigned long)__builtin_frame_address(0); |
117 | frame.sp = current_sp; | 116 | frame.sp = current_stack_pointer; |
118 | frame.pc = (unsigned long)save_stack_trace_tsk; | 117 | frame.pc = (unsigned long)save_stack_trace_tsk; |
119 | } | 118 | } |
120 | 119 | ||
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c index 55a99b9a97e0..13ad4dbb1615 100644 --- a/arch/arm64/kernel/suspend.c +++ b/arch/arm64/kernel/suspend.c | |||
@@ -9,22 +9,19 @@ | |||
9 | #include <asm/suspend.h> | 9 | #include <asm/suspend.h> |
10 | #include <asm/tlbflush.h> | 10 | #include <asm/tlbflush.h> |
11 | 11 | ||
12 | extern int __cpu_suspend(unsigned long); | 12 | extern int __cpu_suspend_enter(unsigned long arg, int (*fn)(unsigned long)); |
13 | /* | 13 | /* |
14 | * This is called by __cpu_suspend() to save the state, and do whatever | 14 | * This is called by __cpu_suspend_enter() to save the state, and do whatever |
15 | * flushing is required to ensure that when the CPU goes to sleep we have | 15 | * flushing is required to ensure that when the CPU goes to sleep we have |
16 | * the necessary data available when the caches are not searched. | 16 | * the necessary data available when the caches are not searched. |
17 | * | 17 | * |
18 | * @arg: Argument to pass to suspend operations | 18 | * ptr: CPU context virtual address |
19 | * @ptr: CPU context virtual address | 19 | * save_ptr: address of the location where the context physical address |
20 | * @save_ptr: address of the location where the context physical address | 20 | * must be saved |
21 | * must be saved | ||
22 | */ | 21 | */ |
23 | int __cpu_suspend_finisher(unsigned long arg, struct cpu_suspend_ctx *ptr, | 22 | void notrace __cpu_suspend_save(struct cpu_suspend_ctx *ptr, |
24 | phys_addr_t *save_ptr) | 23 | phys_addr_t *save_ptr) |
25 | { | 24 | { |
26 | int cpu = smp_processor_id(); | ||
27 | |||
28 | *save_ptr = virt_to_phys(ptr); | 25 | *save_ptr = virt_to_phys(ptr); |
29 | 26 | ||
30 | cpu_do_suspend(ptr); | 27 | cpu_do_suspend(ptr); |
@@ -35,8 +32,6 @@ int __cpu_suspend_finisher(unsigned long arg, struct cpu_suspend_ctx *ptr, | |||
35 | */ | 32 | */ |
36 | __flush_dcache_area(ptr, sizeof(*ptr)); | 33 | __flush_dcache_area(ptr, sizeof(*ptr)); |
37 | __flush_dcache_area(save_ptr, sizeof(*save_ptr)); | 34 | __flush_dcache_area(save_ptr, sizeof(*save_ptr)); |
38 | |||
39 | return cpu_ops[cpu]->cpu_suspend(arg); | ||
40 | } | 35 | } |
41 | 36 | ||
42 | /* | 37 | /* |
@@ -56,15 +51,15 @@ void __init cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)) | |||
56 | } | 51 | } |
57 | 52 | ||
58 | /** | 53 | /** |
59 | * cpu_suspend | 54 | * cpu_suspend() - function to enter a low-power state |
55 | * @arg: argument to pass to CPU suspend operations | ||
60 | * | 56 | * |
61 | * @arg: argument to pass to the finisher function | 57 | * Return: 0 on success, -EOPNOTSUPP if CPU suspend hook not initialized, CPU |
58 | * operations back-end error code otherwise. | ||
62 | */ | 59 | */ |
63 | int cpu_suspend(unsigned long arg) | 60 | int cpu_suspend(unsigned long arg) |
64 | { | 61 | { |
65 | struct mm_struct *mm = current->active_mm; | 62 | int cpu = smp_processor_id(); |
66 | int ret, cpu = smp_processor_id(); | ||
67 | unsigned long flags; | ||
68 | 63 | ||
69 | /* | 64 | /* |
70 | * If cpu_ops have not been registered or suspend | 65 | * If cpu_ops have not been registered or suspend |
@@ -72,6 +67,21 @@ int cpu_suspend(unsigned long arg) | |||
72 | */ | 67 | */ |
73 | if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_suspend) | 68 | if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_suspend) |
74 | return -EOPNOTSUPP; | 69 | return -EOPNOTSUPP; |
70 | return cpu_ops[cpu]->cpu_suspend(arg); | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * __cpu_suspend | ||
75 | * | ||
76 | * arg: argument to pass to the finisher function | ||
77 | * fn: finisher function pointer | ||
78 | * | ||
79 | */ | ||
80 | int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) | ||
81 | { | ||
82 | struct mm_struct *mm = current->active_mm; | ||
83 | int ret; | ||
84 | unsigned long flags; | ||
75 | 85 | ||
76 | /* | 86 | /* |
77 | * From this point debug exceptions are disabled to prevent | 87 | * From this point debug exceptions are disabled to prevent |
@@ -86,7 +96,7 @@ int cpu_suspend(unsigned long arg) | |||
86 | * page tables, so that the thread address space is properly | 96 | * page tables, so that the thread address space is properly |
87 | * set-up on function return. | 97 | * set-up on function return. |
88 | */ | 98 | */ |
89 | ret = __cpu_suspend(arg); | 99 | ret = __cpu_suspend_enter(arg, fn); |
90 | if (ret == 0) { | 100 | if (ret == 0) { |
91 | cpu_switch_mm(mm->pgd, mm); | 101 | cpu_switch_mm(mm->pgd, mm); |
92 | flush_tlb_all(); | 102 | flush_tlb_all(); |
@@ -95,7 +105,7 @@ int cpu_suspend(unsigned long arg) | |||
95 | * Restore per-cpu offset before any kernel | 105 | * Restore per-cpu offset before any kernel |
96 | * subsystem relying on it has a chance to run. | 106 | * subsystem relying on it has a chance to run. |
97 | */ | 107 | */ |
98 | set_my_cpu_offset(per_cpu_offset(cpu)); | 108 | set_my_cpu_offset(per_cpu_offset(smp_processor_id())); |
99 | 109 | ||
100 | /* | 110 | /* |
101 | * Restore HW breakpoint registers to sane values | 111 | * Restore HW breakpoint registers to sane values |
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 02cd3f023e9a..de1b085e7963 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c | |||
@@ -132,7 +132,6 @@ static void dump_instr(const char *lvl, struct pt_regs *regs) | |||
132 | static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | 132 | static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) |
133 | { | 133 | { |
134 | struct stackframe frame; | 134 | struct stackframe frame; |
135 | const register unsigned long current_sp asm ("sp"); | ||
136 | 135 | ||
137 | pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); | 136 | pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); |
138 | 137 | ||
@@ -145,7 +144,7 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | |||
145 | frame.pc = regs->pc; | 144 | frame.pc = regs->pc; |
146 | } else if (tsk == current) { | 145 | } else if (tsk == current) { |
147 | frame.fp = (unsigned long)__builtin_frame_address(0); | 146 | frame.fp = (unsigned long)__builtin_frame_address(0); |
148 | frame.sp = current_sp; | 147 | frame.sp = current_stack_pointer; |
149 | frame.pc = (unsigned long)dump_backtrace; | 148 | frame.pc = (unsigned long)dump_backtrace; |
150 | } else { | 149 | } else { |
151 | /* | 150 | /* |