Greg Kroah-Hartman | b244131 | 2017-11-01 15:07:57 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
Jason Baron | d9f5ab7 | 2010-09-17 11:09:22 -0400 | [diff] [blame] | 2 | /* |
| 3 | * jump label x86 support |
| 4 | * |
| 5 | * Copyright (C) 2009 Jason Baron <jbaron@redhat.com> |
| 6 | * |
| 7 | */ |
| 8 | #include <linux/jump_label.h> |
| 9 | #include <linux/memory.h> |
| 10 | #include <linux/uaccess.h> |
| 11 | #include <linux/module.h> |
| 12 | #include <linux/list.h> |
| 13 | #include <linux/jhash.h> |
| 14 | #include <linux/cpu.h> |
| 15 | #include <asm/kprobes.h> |
| 16 | #include <asm/alternative.h> |
Andy Lutomirski | 35de5b0 | 2016-04-26 12:23:24 -0700 | [diff] [blame] | 17 | #include <asm/text-patching.h> |
Peter Zijlstra | e7bf1ba | 2021-05-06 21:34:01 +0200 | [diff] [blame] | 18 | #include <asm/insn.h> |
Jason Baron | d9f5ab7 | 2010-09-17 11:09:22 -0400 | [diff] [blame] | 19 | |
Peter Zijlstra | fa5e5dc | 2021-05-06 21:33:58 +0200 | [diff] [blame] | 20 | int arch_jump_entry_size(struct jump_entry *entry) |
| 21 | { |
Peter Zijlstra | e7bf1ba | 2021-05-06 21:34:01 +0200 | [diff] [blame] | 22 | struct insn insn = {}; |
| 23 | |
| 24 | insn_decode_kernel(&insn, (void *)jump_entry_code(entry)); |
| 25 | BUG_ON(insn.length != 2 && insn.length != 5); |
| 26 | |
| 27 | return insn.length; |
Peter Zijlstra | fa5e5dc | 2021-05-06 21:33:58 +0200 | [diff] [blame] | 28 | } |
| 29 | |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 30 | struct jump_label_patch { |
| 31 | const void *code; |
| 32 | int size; |
| 33 | }; |
| 34 | |
| 35 | static struct jump_label_patch |
| 36 | __jump_label_patch(struct jump_entry *entry, enum jump_label_type type) |
Jason Baron | d9f5ab7 | 2010-09-17 11:09:22 -0400 | [diff] [blame] | 37 | { |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 38 | const void *expect, *code, *nop; |
Peter Zijlstra | 63f62ad | 2019-10-03 14:50:42 +0200 | [diff] [blame] | 39 | const void *addr, *dest; |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 40 | int size; |
Ard Biesheuvel | 9fc0f79 | 2018-09-18 23:51:40 -0700 | [diff] [blame] | 41 | |
Peter Zijlstra | 63f62ad | 2019-10-03 14:50:42 +0200 | [diff] [blame] | 42 | addr = (void *)jump_entry_code(entry); |
| 43 | dest = (void *)jump_entry_target(entry); |
Peter Zijlstra | 18cbc8b | 2019-08-26 13:38:58 +0200 | [diff] [blame] | 44 | |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 45 | size = arch_jump_entry_size(entry); |
| 46 | switch (size) { |
| 47 | case JMP8_INSN_SIZE: |
| 48 | code = text_gen_insn(JMP8_INSN_OPCODE, addr, dest); |
| 49 | nop = x86_nops[size]; |
| 50 | break; |
| 51 | |
| 52 | case JMP32_INSN_SIZE: |
| 53 | code = text_gen_insn(JMP32_INSN_OPCODE, addr, dest); |
| 54 | nop = x86_nops[size]; |
| 55 | break; |
| 56 | |
| 57 | default: BUG(); |
| 58 | } |
Jason Baron | d9f5ab7 | 2010-09-17 11:09:22 -0400 | [diff] [blame] | 59 | |
Peter Zijlstra | f9510fa | 2021-05-06 21:33:57 +0200 | [diff] [blame] | 60 | if (type == JUMP_LABEL_JMP) |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 61 | expect = nop; |
Peter Zijlstra | f9510fa | 2021-05-06 21:33:57 +0200 | [diff] [blame] | 62 | else |
| 63 | expect = code; |
Jeremy Fitzhardinge | e71a5be | 2011-09-29 11:11:09 -0700 | [diff] [blame] | 64 | |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 65 | if (memcmp(addr, expect, size)) { |
Peter Zijlstra | f9510fa | 2021-05-06 21:33:57 +0200 | [diff] [blame] | 66 | /* |
| 67 | * The location is not an op that we were expecting. |
| 68 | * Something went wrong. Crash the box, as something could be |
| 69 | * corrupting the kernel. |
| 70 | */ |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 71 | pr_crit("jump_label: Fatal kernel bug, unexpected op at %pS [%p] (%5ph != %5ph)) size:%d type:%d\n", |
| 72 | addr, addr, addr, expect, size, type); |
Peter Zijlstra | f9510fa | 2021-05-06 21:33:57 +0200 | [diff] [blame] | 73 | BUG(); |
| 74 | } |
Ard Biesheuvel | 9fc0f79 | 2018-09-18 23:51:40 -0700 | [diff] [blame] | 75 | |
Daniel Bristot de Oliveira | 4cc6620 | 2019-06-12 11:57:27 +0200 | [diff] [blame] | 76 | if (type == JUMP_LABEL_NOP) |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 77 | code = nop; |
Peter Zijlstra | 18cbc8b | 2019-08-26 13:38:58 +0200 | [diff] [blame] | 78 | |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 79 | return (struct jump_label_patch){.code = code, .size = size}; |
Daniel Bristot de Oliveira | 4cc6620 | 2019-06-12 11:57:27 +0200 | [diff] [blame] | 80 | } |
| 81 | |
Randy Dunlap | 4de4952 | 2020-03-26 14:16:58 -0700 | [diff] [blame] | 82 | static inline void __jump_label_transform(struct jump_entry *entry, |
Peter Zijlstra | 18cbc8b | 2019-08-26 13:38:58 +0200 | [diff] [blame] | 83 | enum jump_label_type type, |
| 84 | int init) |
Daniel Bristot de Oliveira | 4cc6620 | 2019-06-12 11:57:27 +0200 | [diff] [blame] | 85 | { |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 86 | const struct jump_label_patch jlp = __jump_label_patch(entry, type); |
Daniel Bristot de Oliveira | 4cc6620 | 2019-06-12 11:57:27 +0200 | [diff] [blame] | 87 | |
Jiri Kosina | 51b2c07 | 2013-07-12 11:22:09 +0200 | [diff] [blame] | 88 | /* |
Nadav Amit | bb0a008 | 2019-04-25 17:11:32 -0700 | [diff] [blame] | 89 | * As long as only a single processor is running and the code is still |
| 90 | * not marked as RO, text_poke_early() can be used; Checking that |
| 91 | * system_state is SYSTEM_BOOTING guarantees it. It will be set to |
| 92 | * SYSTEM_SCHEDULING before other cores are awaken and before the |
| 93 | * code is write-protected. |
Jiri Kosina | 51b2c07 | 2013-07-12 11:22:09 +0200 | [diff] [blame] | 94 | * |
| 95 | * At the time the change is being done, just ignore whether we |
| 96 | * are doing nop -> jump or jump -> nop transition, and assume |
| 97 | * always nop being the 'currently valid' instruction |
Jiri Kosina | 51b2c07 | 2013-07-12 11:22:09 +0200 | [diff] [blame] | 98 | */ |
Nadav Amit | bb0a008 | 2019-04-25 17:11:32 -0700 | [diff] [blame] | 99 | if (init || system_state == SYSTEM_BOOTING) { |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 100 | text_poke_early((void *)jump_entry_code(entry), jlp.code, jlp.size); |
Ard Biesheuvel | 9fc0f79 | 2018-09-18 23:51:40 -0700 | [diff] [blame] | 101 | return; |
| 102 | } |
| 103 | |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 104 | text_poke_bp((void *)jump_entry_code(entry), jlp.code, jlp.size, NULL); |
Peter Zijlstra | 18cbc8b | 2019-08-26 13:38:58 +0200 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | static void __ref jump_label_transform(struct jump_entry *entry, |
| 108 | enum jump_label_type type, |
| 109 | int init) |
| 110 | { |
| 111 | mutex_lock(&text_mutex); |
| 112 | __jump_label_transform(entry, type, init); |
| 113 | mutex_unlock(&text_mutex); |
Jeremy Fitzhardinge | e71a5be | 2011-09-29 11:11:09 -0700 | [diff] [blame] | 114 | } |
| 115 | |
| 116 | void arch_jump_label_transform(struct jump_entry *entry, |
| 117 | enum jump_label_type type) |
| 118 | { |
Peter Zijlstra | 18cbc8b | 2019-08-26 13:38:58 +0200 | [diff] [blame] | 119 | jump_label_transform(entry, type, 0); |
Jason Baron | d9f5ab7 | 2010-09-17 11:09:22 -0400 | [diff] [blame] | 120 | } |
| 121 | |
Daniel Bristot de Oliveira | ba54f0c | 2019-06-12 11:57:31 +0200 | [diff] [blame] | 122 | bool arch_jump_label_transform_queue(struct jump_entry *entry, |
| 123 | enum jump_label_type type) |
| 124 | { |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 125 | struct jump_label_patch jlp; |
Daniel Bristot de Oliveira | ba54f0c | 2019-06-12 11:57:31 +0200 | [diff] [blame] | 126 | |
| 127 | if (system_state == SYSTEM_BOOTING) { |
| 128 | /* |
| 129 | * Fallback to the non-batching mode. |
| 130 | */ |
| 131 | arch_jump_label_transform(entry, type); |
| 132 | return true; |
| 133 | } |
| 134 | |
Peter Zijlstra | 18cbc8b | 2019-08-26 13:38:58 +0200 | [diff] [blame] | 135 | mutex_lock(&text_mutex); |
Peter Zijlstra | 001951b | 2021-05-06 21:33:59 +0200 | [diff] [blame] | 136 | jlp = __jump_label_patch(entry, type); |
| 137 | text_poke_queue((void *)jump_entry_code(entry), jlp.code, jlp.size, NULL); |
Peter Zijlstra | 18cbc8b | 2019-08-26 13:38:58 +0200 | [diff] [blame] | 138 | mutex_unlock(&text_mutex); |
Daniel Bristot de Oliveira | ba54f0c | 2019-06-12 11:57:31 +0200 | [diff] [blame] | 139 | return true; |
| 140 | } |
| 141 | |
| 142 | void arch_jump_label_transform_apply(void) |
| 143 | { |
Daniel Bristot de Oliveira | ba54f0c | 2019-06-12 11:57:31 +0200 | [diff] [blame] | 144 | mutex_lock(&text_mutex); |
Peter Zijlstra | 18cbc8b | 2019-08-26 13:38:58 +0200 | [diff] [blame] | 145 | text_poke_finish(); |
Daniel Bristot de Oliveira | ba54f0c | 2019-06-12 11:57:31 +0200 | [diff] [blame] | 146 | mutex_unlock(&text_mutex); |
Daniel Bristot de Oliveira | ba54f0c | 2019-06-12 11:57:31 +0200 | [diff] [blame] | 147 | } |
| 148 | |
Steven Rostedt | 11570da | 2012-01-26 18:16:15 -0500 | [diff] [blame] | 149 | static enum { |
| 150 | JL_STATE_START, |
| 151 | JL_STATE_NO_UPDATE, |
| 152 | JL_STATE_UPDATE, |
| 153 | } jlstate __initdata_or_module = JL_STATE_START; |
| 154 | |
Peter Zijlstra | 9cdbe1c | 2011-12-06 17:27:29 +0100 | [diff] [blame] | 155 | __init_or_module void arch_jump_label_transform_static(struct jump_entry *entry, |
Jeremy Fitzhardinge | e71a5be | 2011-09-29 11:11:09 -0700 | [diff] [blame] | 156 | enum jump_label_type type) |
| 157 | { |
Steven Rostedt | 11570da | 2012-01-26 18:16:15 -0500 | [diff] [blame] | 158 | if (jlstate == JL_STATE_UPDATE) |
Peter Zijlstra | 18cbc8b | 2019-08-26 13:38:58 +0200 | [diff] [blame] | 159 | jump_label_transform(entry, type, 1); |
Jeremy Fitzhardinge | e71a5be | 2011-09-29 11:11:09 -0700 | [diff] [blame] | 160 | } |