From patchwork Tue May 16 03:49:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Balbir Singh X-Patchwork-Id: 762785 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wRk6n6z0lz9s7B for ; Tue, 16 May 2017 13:52:33 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="BTgbU56W"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3wRk6n5vHQzDqc9 for ; Tue, 16 May 2017 13:52:33 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="BTgbU56W"; dkim-atps=neutral X-Original-To: linuxppc-dev@lists.ozlabs.org Delivered-To: linuxppc-dev@lists.ozlabs.org Received: from mail-pg0-x243.google.com (mail-pg0-x243.google.com [IPv6:2607:f8b0:400e:c05::243]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3wRk3N0yy2zDqYC for ; Tue, 16 May 2017 13:49:36 +1000 (AEST) Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="BTgbU56W"; dkim-atps=neutral Received: by mail-pg0-x243.google.com with SMTP id h64so14916981pge.3 for ; Mon, 15 May 2017 20:49:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=1quhYmwDCzybzOutu/YbqIUaY+cWr06sm04E4v29BJA=; b=BTgbU56WF1B5nPrSlWlGSlfATHxYZfzA9PXLQQiIGUTk7SeMIhvorMEG3dPP+O6bTw vDLt6rVpReQ/C85UW6slaDwAn3G0MGBRc5RYKqqq8OSks15GYpP6dsOhTRbUkr9lXXEw r2K+V/EsuFPHN7Im14p4yIFDPwlH1nlCW08r/KZHQtffOGeQ8DtxEGuxz3IqwY7lbfLp 9jjRvtfrBH4gewLh+sg8n1kEyymJ0vJaeDAk4l98/WGLJIxszZTRG3Md/vFimj3nqodm N/pXLGrnnXjNXbpd8SUzzqnKErti2NulZ52hLGgdlHPQRTMsKMJSib0MlHcwbaPkXWqA wIyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=1quhYmwDCzybzOutu/YbqIUaY+cWr06sm04E4v29BJA=; b=Ww8h0/cBARFZSwer5M3BigI4yCLPlYgzbTXYY1K4DuHPN9NwtiQYCih2tBR6LR3oEd 1EADHTpgcPaggUypz+GGnn12nAjtDBb2lBoca2N+8Dtvfu/tlsGXZTqxqbcmkwoRRep9 Ml2mnNj0J1RbY4+E7NZXkBQ0dNHJP4QuHdXnRdNElV/uXUUDBZGziwVmKC+WyUhduZ7R 88pm6oARHrEwtn+qq2e+AxKvfbsurU4mEpE3GT14B11HPglLdt+1VkBPt5Oa3JscrRca m/s8Sjr7BFTmNqvHOR/vmbpEzstz5ENEmBcg+vuCYoQ1+RIW+qBwpyw0BJcpv/ureqmX oWaA== X-Gm-Message-State: AODbwcCCKKdcYnXX2jE18tF7Jh5GxGoZx04AitS95WwatnjLugm32Xah S2Me/kdP5Ja4bQ== X-Received: by 10.84.135.129 with SMTP id 1mr12981850plj.57.1494906574480; Mon, 15 May 2017 20:49:34 -0700 (PDT) Received: from MiWiFi-R3-srv.au.ibm.com (14-202-194-140.static.tpgi.com.au. [14.202.194.140]) by smtp.gmail.com with ESMTPSA id n71sm26031334pfg.46.2017.05.15.20.49.30 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 15 May 2017 20:49:32 -0700 (PDT) From: Balbir Singh To: linuxppc-dev@lists.ozlabs.org, mpe@ellerman.id.au Subject: [RFC 1/2] powerpc/lib/code-patching: Enhance code patching Date: Tue, 16 May 2017 13:49:12 +1000 Message-Id: <20170516034913.21163-2-bsingharora@gmail.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20170516034913.21163-1-bsingharora@gmail.com> References: <20170516034913.21163-1-bsingharora@gmail.com> X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: naveen.n.rao@linux.vnet.ibm.com Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" Today our patching happens via direct copy and patch_instruction. The patching code is well contained in the sense that copying bits are limited. While considering implementation of CONFIG_STRICT_RWX, the first requirement is to a create another mapping that will allow for patching. We create the window using text_poke_area, allocated via get_vm_area(), which might be an overkill. We can do per-cpu stuff as well. The downside of these patches that patch_instruction is now synchornized using a lock. Other arches do similar things, but use fixmaps. The reason for not using fixmaps is to make use of any randomization in the future. The code also relies on set_pte_at and pte_clear to do the appropriate tlb flushing. Signed-off-by: Balbir Singh --- arch/powerpc/lib/code-patching.c | 88 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c index 500b0f6..4aae016 100644 --- a/arch/powerpc/lib/code-patching.c +++ b/arch/powerpc/lib/code-patching.c @@ -16,19 +16,98 @@ #include #include #include +#include +#include +struct vm_struct *text_poke_area; +static DEFINE_RAW_SPINLOCK(text_poke_lock); -int patch_instruction(unsigned int *addr, unsigned int instr) +/* + * This is an early_initcall and early_initcalls happen at the right time + * for us, after slab is enabled and before we mark ro pages R/O. In the + * future if get_vm_area is randomized, this will be more flexible than + * fixmap + */ +static int __init setup_text_poke_area(void) { + text_poke_area = get_vm_area(PAGE_SIZE, VM_ALLOC); + if (!text_poke_area) { + WARN_ONCE(1, "could not create area for mapping kernel addrs" + " which allow for patching kernel code\n"); + return 0; + } + pr_info("text_poke area ready...\n"); + raw_spin_lock_init(&text_poke_lock); + return 0; +} + +/* + * This can be called for kernel text or a module. + */ +static int kernel_map_addr(void *addr) +{ + unsigned long pfn; int err; - __put_user_size(instr, addr, 4, err); + if (is_vmalloc_addr(addr)) + pfn = vmalloc_to_pfn(addr); + else + pfn = __pa_symbol(addr) >> PAGE_SHIFT; + + err = map_kernel_page((unsigned long)text_poke_area->addr, + (pfn << PAGE_SHIFT), _PAGE_KERNEL_RW | _PAGE_PRESENT); + pr_devel("Mapped addr %p with pfn %lx\n", text_poke_area->addr, pfn); if (err) - return err; - asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (addr)); + return -1; return 0; } +static inline void kernel_unmap_addr(void *addr) +{ + pte_t *pte; + unsigned long kaddr = (unsigned long)addr; + + pte = pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(kaddr), + kaddr), kaddr), kaddr); + pr_devel("clearing mm %p, pte %p, kaddr %lx\n", &init_mm, pte, kaddr); + pte_clear(&init_mm, kaddr, pte); +} + +int patch_instruction(unsigned int *addr, unsigned int instr) +{ + int err; + unsigned int *dest = NULL; + unsigned long flags; + unsigned long kaddr = (unsigned long)addr; + + /* + * During early early boot patch_instruction is called + * when text_poke_area is not ready, but we still need + * to allow patching. We just do the plain old patching + */ + if (!text_poke_area) { + __put_user_size(instr, addr, 4, err); + asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (addr)); + return 0; + } + + raw_spin_lock_irqsave(&text_poke_lock, flags); + if (kernel_map_addr(addr)) { + err = -1; + goto out; + } + + dest = (unsigned int *)(text_poke_area->addr) + + ((kaddr & ~PAGE_MASK) / sizeof(unsigned int)); + __put_user_size(instr, dest, 4, err); + asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (dest)); + kernel_unmap_addr(text_poke_area->addr); +out: + raw_spin_unlock_irqrestore(&text_poke_lock, flags); + return err; +} +NOKPROBE_SYMBOL(patch_instruction); + int patch_branch(unsigned int *addr, unsigned long target, int flags) { return patch_instruction(addr, create_branch(addr, target, flags)); @@ -514,3 +593,4 @@ static int __init test_code_patching(void) late_initcall(test_code_patching); #endif /* CONFIG_CODE_PATCHING_SELFTEST */ +early_initcall(setup_text_poke_area);