Received: by 10.223.176.5 with SMTP id f5csp751474wra; Wed, 7 Feb 2018 07:01:21 -0800 (PST) X-Google-Smtp-Source: AH8x226J/518KtQMycHltyxPxx20jOoFVpSJBxcyg9D5vkiaGeZYyQLYt1rnZXcWx/ZavC6gfyJt X-Received: by 2002:a17:902:6843:: with SMTP id f3-v6mr6241947pln.182.1518015681814; Wed, 07 Feb 2018 07:01:21 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1518015681; cv=none; d=google.com; s=arc-20160816; b=QIDbR5ppkuppHc6ckNJGMssRHo5ER1jK/s0maLXE9Q3yRSfIf6I11rJdCZpOqgjm9P wrx6JKAej9uV8F1p+WAaMHkEhLGR9tUdevyfkADl1CXqN3a5tSCk9rJBBxh8hba7g10t TX/dD8nygYV2taJVk/u859PU5q0t74m0vyOU/kMR1D6/XEYILK9uCeQIS+Bl7ZQpoIa4 r84pUW/Mdjis4f6nvMXJQEeOwyJaDehsW/vgijjMj7utKBAZ9vjlXN2PYlC2E/9S65An ThWvzD7kKRW/l6Jse55ko/Oiks8wJfhBoTIeLPkHPzKXVzruWQ35hnOm4W21qL4Dw+M+ h0LQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=XlXw+kDuoHhnUPnwfAAJnv9nkwT4sM3GsmYUDm8ZTjg=; b=wTCw4vPKSpXsr0iLjSrSIWNquIO7FtK1QK72E8YEo0Tt78n67UYGbm3/06W/xlPkdQ dCgnNUJ9y+jF3Lp9V/iX7UPAFBhA5JOpoZN62JEJL3xFpSILZx/OUv4kwLcR2QDcG7lj ENGqIA9yTQVwxxUd5VyqCU3UzRV02cKfBMwki03Ko++gybBVJJZNdfVXztdudzATUlOX lAAX1jia5bewTSOqJIKNuRJq6WeSJg90B+04vNx4LeY3v6vQPnch02snWCEkN/iR6mQD qktBKdfBmKb+IwhvWdKTJzBnn6gth+cgV3qpWum+EP90lMngKoN0ldx765vgBgmaZtqL NUdA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id f6-v6si1205023pln.303.2018.02.07.07.01.07; Wed, 07 Feb 2018 07:01:21 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754466AbeBGO73 (ORCPT + 99 others); Wed, 7 Feb 2018 09:59:29 -0500 Received: from mga17.intel.com ([192.55.52.151]:3908 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754108AbeBGO72 (ORCPT ); Wed, 7 Feb 2018 09:59:28 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 07 Feb 2018 06:59:28 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.46,473,1511856000"; d="scan'208";a="17976783" Received: from black.fi.intel.com ([10.237.72.28]) by fmsmga002.fm.intel.com with ESMTP; 07 Feb 2018 06:59:25 -0800 Received: by black.fi.intel.com (Postfix, from userid 1000) id 94E9F76; Wed, 7 Feb 2018 16:59:24 +0200 (EET) From: "Kirill A. Shutemov" To: Linus Torvalds , x86@kernel.org, Tom Lendacky , Peter Zijlstra Cc: Dave Hansen , Andy Lutomirski , Borislav Petkov , linux-mm@kvack.org, linux-kernel@vger.kernel.org, "Kirill A. Shutemov" Subject: [RFC 1/3] x86: Introduce patchable constants Date: Wed, 7 Feb 2018 17:59:11 +0300 Message-Id: <20180207145913.2703-2-kirill.shutemov@linux.intel.com> X-Mailer: git-send-email 2.15.1 In-Reply-To: <20180207145913.2703-1-kirill.shutemov@linux.intel.com> References: <20180207145913.2703-1-kirill.shutemov@linux.intel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch introduces concept of patchable constants: constant values that can be adjusted at boot-time in response to system configuration or user input (kernel command-line). Patchable constants can replace variables that never changes at runtime (only at boot-time), but used in very hot path. Patchable constants implemented by replacing a constant with call to inline function that returns the constant value using inline assembler. In inline assembler we also write down into separate section location of the instruction that loads the constant. This way we can find the location later and adjust the value. The implementation only supports unsigned 64-bit values on 64-bit systems. We can add support for other data types later. Signed-off-by: Kirill A. Shutemov --- arch/x86/Kconfig | 4 ++ arch/x86/include/asm/patchable_const.h | 28 ++++++++ arch/x86/kernel/Makefile | 3 + arch/x86/kernel/module.c | 14 ++++ arch/x86/kernel/patchable_const.c | 114 +++++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+) create mode 100644 arch/x86/include/asm/patchable_const.h create mode 100644 arch/x86/kernel/patchable_const.c diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b0771ceabb4b..78fc28e4f643 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -337,6 +337,10 @@ config PGTABLE_LEVELS default 3 if X86_PAE default 2 +config PATCHABLE_CONST + bool + depends on X86_64 + source "init/Kconfig" source "kernel/Kconfig.freezer" diff --git a/arch/x86/include/asm/patchable_const.h b/arch/x86/include/asm/patchable_const.h new file mode 100644 index 000000000000..a432da46a46e --- /dev/null +++ b/arch/x86/include/asm/patchable_const.h @@ -0,0 +1,28 @@ +#ifndef __X86_PATCHABLE_CONST +#define __X86_PATCHABLE_CONST + +#ifndef __ASSEMBLY__ + +#include +#include +#include + +void module_patch_const_u64(const char *name, + unsigned long **start, unsigned long **stop); + +#define DECLARE_PATCHABLE_CONST_U64(id_str) \ +extern int id_str ## _SET(u64 value); \ +static __always_inline __attribute_const__ u64 id_str ## _READ(void) \ +{ \ + u64 ret; \ + asm ( \ + "1: movabsq $(" __stringify(id_str ## _DEFAULT) "), %0\n" \ + ".pushsection \"const_u64_" __stringify(id_str) "\",\"a\"\n" \ + _ASM_PTR "1b\n" \ + ".popsection\n" : "=r" (ret)); \ + return ret; \ +} + +#endif /* __ASSEMBLY__ */ + +#endif diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 29786c87e864..e6a2e400f236 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -20,6 +20,7 @@ CFLAGS_REMOVE_kvmclock.o = -pg CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_early_printk.o = -pg CFLAGS_REMOVE_head64.o = -pg +CFLAGS_REMOVE_patchable_const.o = -pg endif KASAN_SANITIZE_head$(BITS).o := n @@ -27,6 +28,7 @@ KASAN_SANITIZE_dumpstack.o := n KASAN_SANITIZE_dumpstack_$(BITS).o := n KASAN_SANITIZE_stacktrace.o := n KASAN_SANITIZE_paravirt.o := n +KASAN_SANITIZE_patchable_const.o := n OBJECT_FILES_NON_STANDARD_relocate_kernel_$(BITS).o := y OBJECT_FILES_NON_STANDARD_test_nx.o := y @@ -110,6 +112,7 @@ obj-$(CONFIG_AMD_NB) += amd_nb.o obj-$(CONFIG_DEBUG_NMI_SELFTEST) += nmi_selftest.o obj-$(CONFIG_KVM_GUEST) += kvm.o kvmclock.o +obj-$(CONFIG_PATCHABLE_CONST) += patchable_const.o obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= paravirt-spinlocks.o obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c index da0c160e5589..eeb80b39fa89 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -36,6 +36,7 @@ #include #include #include +#include #if 0 #define DEBUGP(fmt, ...) \ @@ -243,6 +244,19 @@ int module_finalize(const Elf_Ehdr *hdr, orc = s; if (!strcmp(".orc_unwind_ip", secstrings + s->sh_name)) orc_ip = s; + + if (IS_ENABLED(CONFIG_PATCHABLE_CONST) && + !strncmp(secstrings + s->sh_name, + "const_u64_", strlen("const_u64_"))) { + const char *name; + unsigned long **start, **stop; + + name = secstrings + s->sh_name + strlen("const_u64_"); + start = (void *)s->sh_addr; + stop = (void *)s->sh_addr + s->sh_size; + + module_patch_const_u64(name, start, stop); + } } if (alt) { diff --git a/arch/x86/kernel/patchable_const.c b/arch/x86/kernel/patchable_const.c new file mode 100644 index 000000000000..d44d91cafee2 --- /dev/null +++ b/arch/x86/kernel/patchable_const.c @@ -0,0 +1,114 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +struct const_u64_table { + const char *name; + u64 orig; + u64 *new; +}; + +#define PATCHABLE_CONST_U64(id_str) \ +extern unsigned long *__start_const_u64_ ## id_str[]; \ +extern unsigned long *__stop_const_u64_ ## id_str[]; \ +static __init_or_module u64 id_str ## _CURRENT = id_str ## _DEFAULT; \ +__init __nostackprotector int id_str ## _SET(u64 new) \ +{ \ + int ret; \ + ret = patch_const_u64(__start_const_u64_ ## id_str, \ + __stop_const_u64_ ## id_str, id_str ## _CURRENT, new); \ + if (!ret) \ + id_str ## _CURRENT = new; \ + return ret; \ +} + +static __init_or_module __nostackprotector +int patch_const_u64(unsigned long **start, unsigned long **stop, + u64 orig, u64 new) +{ + char buf[MAX_INSN_SIZE]; + struct insn insn; + unsigned long **iter; + + pr_debug("Patch const: %#llx -> %#llx\n", orig, new); + + mutex_lock(&text_mutex); + for (iter = start; iter < stop; iter++) { + memcpy(buf, *iter, MAX_INSN_SIZE); + + kernel_insn_init(&insn, buf, MAX_INSN_SIZE); + insn_get_length(&insn); + + /* + * We expect to see 10-byte MOV instruction here: + * - 1 byte REX prefix; + * - 1 byte opcode; + * - 8 byte immediate value; + * + * Back off, if something else is found. + */ + if (insn.length != 10) + break; + + insn_get_opcode(&insn); + + /* MOV r64, imm64: REX.W + B8 + rd io */ + if (!X86_REX_W(insn.rex_prefix.bytes[0])) + break; + if ((insn.opcode.bytes[0] & ~7) != 0xb8) + break; + + /* Check that the original value is correct */ + if (memcmp(buf + 2, &orig, sizeof(orig))) + break; + + memcpy(buf + 2, &new, 8); + text_poke(*iter, buf, 10); + } + + if (iter == stop) { + /* Everything if fine: DONE */ + mutex_unlock(&text_mutex); + return 0; + } + + /* Something went wrong. */ + pr_err("Unexpected instruction found at %px: %10ph\n", iter, buf); + + /* Undo */ + while (--iter != start) { + memcpy(&buf, *iter, MAX_INSN_SIZE); + memcpy(buf + 2, &orig, 8); + text_poke(*iter, buf, 10); + } + + mutex_unlock(&text_mutex); + return -EFAULT; +} + +#ifdef CONFIG_MODULES +/* Add an entry for a constant here if it expected to be seen in the modules */ +static const struct const_u64_table const_u64_table[] = { +}; + +__init_or_module __nostackprotector +void module_patch_const_u64(const char *name, + unsigned long **start, unsigned long **stop) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(const_u64_table); i++) { + if (strcmp(name, const_u64_table[i].name)) + continue; + + patch_const_u64(start, stop, const_u64_table[i].orig, + *const_u64_table[i].new); + return; + } + + pr_err("Unknown patchable constant: '%s'\n", name); +} +#endif -- 2.15.1