18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _ASM_X86_TEXT_PATCHING_H
38c2ecf20Sopenharmony_ci#define _ASM_X86_TEXT_PATCHING_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/types.h>
68c2ecf20Sopenharmony_ci#include <linux/stddef.h>
78c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_cistruct paravirt_patch_site;
108c2ecf20Sopenharmony_ci#ifdef CONFIG_PARAVIRT
118c2ecf20Sopenharmony_civoid apply_paravirt(struct paravirt_patch_site *start,
128c2ecf20Sopenharmony_ci		    struct paravirt_patch_site *end);
138c2ecf20Sopenharmony_ci#else
148c2ecf20Sopenharmony_cistatic inline void apply_paravirt(struct paravirt_patch_site *start,
158c2ecf20Sopenharmony_ci				  struct paravirt_patch_site *end)
168c2ecf20Sopenharmony_ci{}
178c2ecf20Sopenharmony_ci#define __parainstructions	NULL
188c2ecf20Sopenharmony_ci#define __parainstructions_end	NULL
198c2ecf20Sopenharmony_ci#endif
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * Currently, the max observed size in the kernel code is
238c2ecf20Sopenharmony_ci * JUMP_LABEL_NOP_SIZE/RELATIVEJUMP_SIZE, which are 5.
248c2ecf20Sopenharmony_ci * Raise it if needed.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci#define POKE_MAX_OPCODE_SIZE	5
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ciextern void text_poke_early(void *addr, const void *opcode, size_t len);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * Clear and restore the kernel write-protection flag on the local CPU.
328c2ecf20Sopenharmony_ci * Allows the kernel to edit read-only pages.
338c2ecf20Sopenharmony_ci * Side-effect: any interrupt handler running between save and restore will have
348c2ecf20Sopenharmony_ci * the ability to write to read-only pages.
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * Warning:
378c2ecf20Sopenharmony_ci * Code patching in the UP case is safe if NMIs and MCE handlers are stopped and
388c2ecf20Sopenharmony_ci * no thread can be preempted in the instructions being modified (no iret to an
398c2ecf20Sopenharmony_ci * invalid instruction possible) or if the instructions are changed from a
408c2ecf20Sopenharmony_ci * consistent state to another consistent state atomically.
418c2ecf20Sopenharmony_ci * On the local CPU you need to be protected against NMI or MCE handlers seeing
428c2ecf20Sopenharmony_ci * an inconsistent instruction while you patch.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ciextern void *text_poke(void *addr, const void *opcode, size_t len);
458c2ecf20Sopenharmony_ciextern void text_poke_sync(void);
468c2ecf20Sopenharmony_ciextern void *text_poke_kgdb(void *addr, const void *opcode, size_t len);
478c2ecf20Sopenharmony_ciextern int poke_int3_handler(struct pt_regs *regs);
488c2ecf20Sopenharmony_ciextern void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulate);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ciextern void text_poke_queue(void *addr, const void *opcode, size_t len, const void *emulate);
518c2ecf20Sopenharmony_ciextern void text_poke_finish(void);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define INT3_INSN_SIZE		1
548c2ecf20Sopenharmony_ci#define INT3_INSN_OPCODE	0xCC
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define RET_INSN_SIZE		1
578c2ecf20Sopenharmony_ci#define RET_INSN_OPCODE		0xC3
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define CALL_INSN_SIZE		5
608c2ecf20Sopenharmony_ci#define CALL_INSN_OPCODE	0xE8
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define JMP32_INSN_SIZE		5
638c2ecf20Sopenharmony_ci#define JMP32_INSN_OPCODE	0xE9
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define JMP8_INSN_SIZE		2
668c2ecf20Sopenharmony_ci#define JMP8_INSN_OPCODE	0xEB
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define DISP32_SIZE		4
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic __always_inline int text_opcode_size(u8 opcode)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	int size = 0;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define __CASE(insn)	\
758c2ecf20Sopenharmony_ci	case insn##_INSN_OPCODE: size = insn##_INSN_SIZE; break
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	switch(opcode) {
788c2ecf20Sopenharmony_ci	__CASE(INT3);
798c2ecf20Sopenharmony_ci	__CASE(RET);
808c2ecf20Sopenharmony_ci	__CASE(CALL);
818c2ecf20Sopenharmony_ci	__CASE(JMP32);
828c2ecf20Sopenharmony_ci	__CASE(JMP8);
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#undef __CASE
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return size;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ciunion text_poke_insn {
918c2ecf20Sopenharmony_ci	u8 text[POKE_MAX_OPCODE_SIZE];
928c2ecf20Sopenharmony_ci	struct {
938c2ecf20Sopenharmony_ci		u8 opcode;
948c2ecf20Sopenharmony_ci		s32 disp;
958c2ecf20Sopenharmony_ci	} __attribute__((packed));
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic __always_inline
998c2ecf20Sopenharmony_civoid *text_gen_insn(u8 opcode, const void *addr, const void *dest)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	static union text_poke_insn insn; /* per instance */
1028c2ecf20Sopenharmony_ci	int size = text_opcode_size(opcode);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	insn.opcode = opcode;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (size > 1) {
1078c2ecf20Sopenharmony_ci		insn.disp = (long)dest - (long)(addr + size);
1088c2ecf20Sopenharmony_ci		if (size == 2) {
1098c2ecf20Sopenharmony_ci			/*
1108c2ecf20Sopenharmony_ci			 * Ensure that for JMP9 the displacement
1118c2ecf20Sopenharmony_ci			 * actually fits the signed byte.
1128c2ecf20Sopenharmony_ci			 */
1138c2ecf20Sopenharmony_ci			BUG_ON((insn.disp >> 31) != (insn.disp >> 7));
1148c2ecf20Sopenharmony_ci		}
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return &insn.text;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ciextern int after_bootmem;
1218c2ecf20Sopenharmony_ciextern __ro_after_init struct mm_struct *poking_mm;
1228c2ecf20Sopenharmony_ciextern __ro_after_init unsigned long poking_addr;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#ifndef CONFIG_UML_X86
1258c2ecf20Sopenharmony_cistatic __always_inline
1268c2ecf20Sopenharmony_civoid int3_emulate_jmp(struct pt_regs *regs, unsigned long ip)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	regs->ip = ip;
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic __always_inline
1328c2ecf20Sopenharmony_civoid int3_emulate_push(struct pt_regs *regs, unsigned long val)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	/*
1358c2ecf20Sopenharmony_ci	 * The int3 handler in entry_64.S adds a gap between the
1368c2ecf20Sopenharmony_ci	 * stack where the break point happened, and the saving of
1378c2ecf20Sopenharmony_ci	 * pt_regs. We can extend the original stack because of
1388c2ecf20Sopenharmony_ci	 * this gap. See the idtentry macro's create_gap option.
1398c2ecf20Sopenharmony_ci	 *
1408c2ecf20Sopenharmony_ci	 * Similarly entry_32.S will have a gap on the stack for (any) hardware
1418c2ecf20Sopenharmony_ci	 * exception and pt_regs; see FIXUP_FRAME.
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci	regs->sp -= sizeof(unsigned long);
1448c2ecf20Sopenharmony_ci	*(unsigned long *)regs->sp = val;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic __always_inline
1488c2ecf20Sopenharmony_ciunsigned long int3_emulate_pop(struct pt_regs *regs)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	unsigned long val = *(unsigned long *)regs->sp;
1518c2ecf20Sopenharmony_ci	regs->sp += sizeof(unsigned long);
1528c2ecf20Sopenharmony_ci	return val;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic __always_inline
1568c2ecf20Sopenharmony_civoid int3_emulate_call(struct pt_regs *regs, unsigned long func)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	int3_emulate_push(regs, regs->ip - INT3_INSN_SIZE + CALL_INSN_SIZE);
1598c2ecf20Sopenharmony_ci	int3_emulate_jmp(regs, func);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic __always_inline
1638c2ecf20Sopenharmony_civoid int3_emulate_ret(struct pt_regs *regs)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	unsigned long ip = int3_emulate_pop(regs);
1668c2ecf20Sopenharmony_ci	int3_emulate_jmp(regs, ip);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci#endif /* !CONFIG_UML_X86 */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci#endif /* _ASM_X86_TEXT_PATCHING_H */
171