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