1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Jump label s390 support |
4 | * |
5 | * Copyright IBM Corp. 2011 |
6 | * Author(s): Jan Glauber <jang@linux.vnet.ibm.com> |
7 | */ |
8 | #include <linux/uaccess.h> |
9 | #include <linux/jump_label.h> |
10 | #include <linux/module.h> |
11 | #include <asm/text-patching.h> |
12 | #include <asm/ipl.h> |
13 | |
14 | struct insn { |
15 | u16 opcode; |
16 | s32 offset; |
17 | } __packed; |
18 | |
19 | static void jump_label_make_nop(struct jump_entry *entry, struct insn *insn) |
20 | { |
21 | /* brcl 0,offset */ |
22 | insn->opcode = 0xc004; |
23 | insn->offset = (jump_entry_target(entry) - jump_entry_code(entry)) >> 1; |
24 | } |
25 | |
26 | static void jump_label_make_branch(struct jump_entry *entry, struct insn *insn) |
27 | { |
28 | /* brcl 15,offset */ |
29 | insn->opcode = 0xc0f4; |
30 | insn->offset = (jump_entry_target(entry) - jump_entry_code(entry)) >> 1; |
31 | } |
32 | |
33 | static void jump_label_bug(struct jump_entry *entry, struct insn *expected, |
34 | struct insn *new) |
35 | { |
36 | unsigned char *ipc = (unsigned char *)jump_entry_code(entry); |
37 | unsigned char *ipe = (unsigned char *)expected; |
38 | unsigned char *ipn = (unsigned char *)new; |
39 | |
40 | pr_emerg("Jump label code mismatch at %pS [%px]\n" , ipc, ipc); |
41 | pr_emerg("Found: %6ph\n" , ipc); |
42 | pr_emerg("Expected: %6ph\n" , ipe); |
43 | pr_emerg("New: %6ph\n" , ipn); |
44 | panic(fmt: "Corrupted kernel text" ); |
45 | } |
46 | |
47 | static void jump_label_transform(struct jump_entry *entry, |
48 | enum jump_label_type type) |
49 | { |
50 | void *code = (void *)jump_entry_code(entry); |
51 | struct insn old, new; |
52 | |
53 | if (type == JUMP_LABEL_JMP) { |
54 | jump_label_make_nop(entry, insn: &old); |
55 | jump_label_make_branch(entry, insn: &new); |
56 | } else { |
57 | jump_label_make_branch(entry, insn: &old); |
58 | jump_label_make_nop(entry, insn: &new); |
59 | } |
60 | if (memcmp(p: code, q: &old, size: sizeof(old))) |
61 | jump_label_bug(entry, expected: &old, new: &new); |
62 | s390_kernel_write(code, &new, sizeof(new)); |
63 | } |
64 | |
65 | void arch_jump_label_transform(struct jump_entry *entry, |
66 | enum jump_label_type type) |
67 | { |
68 | jump_label_transform(entry, type); |
69 | text_poke_sync(); |
70 | } |
71 | |
72 | bool arch_jump_label_transform_queue(struct jump_entry *entry, |
73 | enum jump_label_type type) |
74 | { |
75 | jump_label_transform(entry, type); |
76 | return true; |
77 | } |
78 | |
79 | void arch_jump_label_transform_apply(void) |
80 | { |
81 | text_poke_sync(); |
82 | } |
83 | |