From owner-svn-src-head@FreeBSD.ORG Wed Jun 3 14:07:52 2015 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 25ED6D7C; Wed, 3 Jun 2015 14:07:52 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 144DD1F16; Wed, 3 Jun 2015 14:07:52 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id t53E7pbY070462; Wed, 3 Jun 2015 14:07:51 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id t53E7pDX070458; Wed, 3 Jun 2015 14:07:51 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201506031407.t53E7pDX070458@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Wed, 3 Jun 2015 14:07:51 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r283947 - head/sys/arm/arm X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 03 Jun 2015 14:07:52 -0000 Author: ian Date: Wed Jun 3 14:07:50 2015 New Revision: 283947 URL: https://svnweb.freebsd.org/changeset/base/283947 Log: Better handling of userland sysarch() requests to flush icache. On armv6, cache maintenance can trigger page faults. Add handling so that these turn into SIGSEGV that kills the process rather than panics that kill the kernel. Differential Revision: https://reviews.freebsd.org/D2035 Submitted by: Michal Meloun Modified: head/sys/arm/arm/cpu_asm-v6.S head/sys/arm/arm/sys_machdep.c head/sys/arm/arm/trap-v6.c Modified: head/sys/arm/arm/cpu_asm-v6.S ============================================================================== --- head/sys/arm/arm/cpu_asm-v6.S Wed Jun 3 13:43:04 2015 (r283946) +++ head/sys/arm/arm/cpu_asm-v6.S Wed Jun 3 14:07:50 2015 (r283947) @@ -26,6 +26,7 @@ * * $FreeBSD$ */ +#include "assym.s" #include #include @@ -33,6 +34,17 @@ #include #include +#if __ARM_ARCH >= 6 +#define GET_PCB(tmp) \ + mrc CP15_TPIDRPRW(tmp); \ + add tmp, tmp, #(TD_PCB) +#else +.Lcurpcb: + .word _C_LABEL(__pcpu) + PC_CURPCB +#define GET_PCB(tmp) \ + ldr tmp, .Lcurpcb +#endif + /* * Define cache functions used by startup code, which counts on the fact that * only r0-r3,r12 (ip) are modified and no stack space is used. These functions @@ -208,3 +220,59 @@ ASENTRY_NP(dcache_wbinv_poc_all) bx lr #endif /* __ARM_ARCH == 6 */ END(dcache_wbinv_poc_all) + +ASENTRY_NP(dcache_wb_pou_checked) + ldr ip, .Lcpuinfo + ldr ip, [ip, #DCACHE_LINE_SIZE] + + GET_PCB(r2) + ldr r2, [r2] + + adr r3, _C_LABEL(cachebailout) + str r3, [r2, #PCB_ONFAULT] +1: + mcr CP15_DCCMVAC(r0) + add r0, r0, ip + subs r1, r1, ip + bhi 1b + DSB + mov r0, #0 + str r0, [r2, #PCB_ONFAULT] + mov r0, #1 /* cannot be faulting address */ + RET + +.Lcpuinfo: + .word cpuinfo +END(dcache_wb_pou_checked) + +ASENTRY_NP(icache_inv_pou_checked) + ldr ip, .Lcpuinfo + ldr ip, [ip, #ICACHE_LINE_SIZE] + + GET_PCB(r2) + ldr r2, [r2] + + adr r3, _C_LABEL(cachebailout) + str r3, [r2, #PCB_ONFAULT] + +1: + mcr CP15_ICIMVAU(r0) + add r0, r0, ip + subs r1, r1, ip + bhi 1b + DSB + ISB + mov r0, #0 + str r0, [r2, #PCB_ONFAULT] + mov r0, #1 /* cannot be faulting address */ + RET +END(icache_inv_pou_checked) + +/* label must be global as trap-v6.c references it */ + .global _C_LABEL(cachebailout) +_C_LABEL(cachebailout): + DSB + ISB + mov r1, #0 + str r1, [r2, #PCB_ONFAULT] + RET Modified: head/sys/arm/arm/sys_machdep.c ============================================================================== --- head/sys/arm/arm/sys_machdep.c Wed Jun 3 13:43:04 2015 (r283946) +++ head/sys/arm/arm/sys_machdep.c Wed Jun 3 14:07:50 2015 (r283947) @@ -41,8 +41,12 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include +#include #include +#include #ifndef _SYS_SYSPROTO_H_ struct sysarch_args { @@ -55,16 +59,89 @@ struct sysarch_args { static int arm32_sync_icache (struct thread *, void *); static int arm32_drain_writebuf(struct thread *, void *); +#if __ARM_ARCH >= 6 +static int +sync_icache(uintptr_t addr, size_t len) +{ + size_t size; + vm_offset_t rv; + + /* + * Align starting address to even number because value of "1" + * is used as return value for success. + */ + len += addr & 1; + addr &= ~1; + + /* Break whole range to pages. */ + do { + size = PAGE_SIZE - (addr & PAGE_MASK); + size = min(size, len); + rv = dcache_wb_pou_checked(addr, size); + if (rv == 1) /* see dcache_wb_pou_checked() */ + rv = icache_inv_pou_checked(addr, size); + if (rv != 1) { + if (!useracc((void *)addr, size, VM_PROT_READ)) { + /* Invalid access */ + return (rv); + } + /* Valid but unmapped page - skip it. */ + } + len -= size; + addr += size; + } while (len > 0); + + /* Invalidate branch predictor buffer. */ + bpb_inv_all(); + return (1); +} +#endif + static int arm32_sync_icache(struct thread *td, void *args) { struct arm_sync_icache_args ua; int error; + ksiginfo_t ksi; +#if __ARM_ARCH >= 6 + vm_offset_t rv; +#endif if ((error = copyin(args, &ua, sizeof(ua))) != 0) return (error); + if (ua.len == 0) { + td->td_retval[0] = 0; + return (0); + } + + /* + * Validate arguments. Address and length are unsigned, + * so we can use wrapped overflow check. + */ + if (((ua.addr + ua.len) < ua.addr) || + ((ua.addr + ua.len) > VM_MAXUSER_ADDRESS)) { + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGSEGV; + ksi.ksi_code = SEGV_ACCERR; + ksi.ksi_addr = (void *)max(ua.addr, VM_MAXUSER_ADDRESS); + trapsignal(td, &ksi); + return (EINVAL); + } + +#if __ARM_ARCH >= 6 + rv = sync_icache(ua.addr, ua.len); + if (rv != 1) { + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGSEGV; + ksi.ksi_code = SEGV_MAPERR; + ksi.ksi_addr = (void *)rv; + trapsignal(td, &ksi); + return (EINVAL); + } +#else cpu_icache_sync_range(ua.addr, ua.len); +#endif td->td_retval[0] = 0; return (0); Modified: head/sys/arm/arm/trap-v6.c ============================================================================== --- head/sys/arm/arm/trap-v6.c Wed Jun 3 13:43:04 2015 (r283946) +++ head/sys/arm/arm/trap-v6.c Wed Jun 3 14:07:50 2015 (r283947) @@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$"); #endif extern char fusubailout[]; +extern char cachebailout[]; #ifdef DEBUG int last_fault_code; /* For the benefit of pmap_fault_fixup() */ @@ -133,7 +134,7 @@ static const struct abort aborts[] = { {abort_align, "Alignment Fault"}, {abort_fatal, "Debug Event"}, {NULL, "Access Bit (L1)"}, - {abort_icache, "Instruction cache maintenance"}, + {NULL, "Instruction cache maintenance"}, {NULL, "Translation Fault (L1)"}, {NULL, "Access Bit (L2)"}, {NULL, "Translation Fault (L2)"}, @@ -406,6 +407,24 @@ abort_handler(struct trapframe *tf, int } /* + * Don't pass faulting cache operation to vm_fault(). We don't want + * to handle all vm stuff at this moment. + */ + pcb = td->td_pcb; + if (__predict_false(pcb->pcb_onfault == cachebailout)) { + tf->tf_r0 = far; /* return failing address */ + tf->tf_pc = (register_t)pcb->pcb_onfault; + return; + } + + /* Handle remaining I cache aborts. */ + if (idx == FAULT_ICACHE) { + if (abort_icache(tf, idx, fsr, far, prefetch, td, &ksig)) + goto do_trapsignal; + goto out; + } + + /* * At this point, we're dealing with one of the following aborts: * * FAULT_TRAN_xx - Translation