1129198Scognet/*- 2129198Scognet * Copyright (c) 1990 The Regents of the University of California. 3129198Scognet * All rights reserved. 4129198Scognet * 5129198Scognet * Redistribution and use in source and binary forms, with or without 6129198Scognet * modification, are permitted provided that the following conditions 7129198Scognet * are met: 8129198Scognet * 1. Redistributions of source code must retain the above copyright 9129198Scognet * notice, this list of conditions and the following disclaimer. 10129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 11129198Scognet * notice, this list of conditions and the following disclaimer in the 12129198Scognet * documentation and/or other materials provided with the distribution. 13263036Simp * 3. Neither the name of the University nor the names of its contributors 14129198Scognet * may be used to endorse or promote products derived from this software 15129198Scognet * without specific prior written permission. 16129198Scognet * 17129198Scognet * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18129198Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19129198Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20129198Scognet * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21129198Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22129198Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23129198Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27129198Scognet * SUCH DAMAGE. 28129198Scognet * 29129198Scognet * from: @(#)sys_machdep.c 5.5 (Berkeley) 1/19/91 30129198Scognet */ 31129198Scognet 32129198Scognet#include <sys/cdefs.h> 33129198Scognet__FBSDID("$FreeBSD: stable/11/sys/arm/arm/sys_machdep.c 331722 2018-03-29 02:50:57Z eadler $"); 34129198Scognet 35223668Sjonathan#include "opt_capsicum.h" 36219134Srwatson 37129198Scognet#include <sys/param.h> 38129198Scognet#include <sys/systm.h> 39263233Srwatson#include <sys/capsicum.h> 40129198Scognet#include <sys/proc.h> 41129198Scognet#include <sys/sysproto.h> 42129198Scognet#include <sys/syscall.h> 43129198Scognet#include <sys/sysent.h> 44283947Sian#include <vm/vm.h> 45283947Sian#include <vm/vm_extern.h> 46129198Scognet 47295315Smmel#include <machine/cpu.h> 48135642Scognet#include <machine/sysarch.h> 49325307Smmel#include <machine/machdep.h> 50283947Sian#include <machine/vmparam.h> 51135642Scognet 52129198Scognet#ifndef _SYS_SYSPROTO_H_ 53129198Scognetstruct sysarch_args { 54129198Scognet int op; 55129198Scognet char *parms; 56129198Scognet}; 57129198Scognet#endif 58129198Scognet 59135642Scognet/* Prototypes */ 60135642Scognetstatic int arm32_sync_icache (struct thread *, void *); 61135642Scognetstatic int arm32_drain_writebuf(struct thread *, void *); 62135642Scognet 63283947Sian#if __ARM_ARCH >= 6 64135642Scognetstatic int 65283947Siansync_icache(uintptr_t addr, size_t len) 66283947Sian{ 67283947Sian size_t size; 68283947Sian vm_offset_t rv; 69283947Sian 70283947Sian /* 71283947Sian * Align starting address to even number because value of "1" 72283947Sian * is used as return value for success. 73283947Sian */ 74283947Sian len += addr & 1; 75283947Sian addr &= ~1; 76283947Sian 77283947Sian /* Break whole range to pages. */ 78283947Sian do { 79283947Sian size = PAGE_SIZE - (addr & PAGE_MASK); 80283947Sian size = min(size, len); 81283947Sian rv = dcache_wb_pou_checked(addr, size); 82283947Sian if (rv == 1) /* see dcache_wb_pou_checked() */ 83283947Sian rv = icache_inv_pou_checked(addr, size); 84283947Sian if (rv != 1) { 85283947Sian if (!useracc((void *)addr, size, VM_PROT_READ)) { 86283947Sian /* Invalid access */ 87283947Sian return (rv); 88283947Sian } 89283947Sian /* Valid but unmapped page - skip it. */ 90283947Sian } 91283947Sian len -= size; 92283947Sian addr += size; 93283947Sian } while (len > 0); 94283947Sian 95283947Sian /* Invalidate branch predictor buffer. */ 96283947Sian bpb_inv_all(); 97283947Sian return (1); 98283947Sian} 99283947Sian#endif 100283947Sian 101283947Sianstatic int 102135642Scognetarm32_sync_icache(struct thread *td, void *args) 103135642Scognet{ 104135642Scognet struct arm_sync_icache_args ua; 105135642Scognet int error; 106283947Sian ksiginfo_t ksi; 107283947Sian#if __ARM_ARCH >= 6 108283947Sian vm_offset_t rv; 109283947Sian#endif 110135642Scognet 111135642Scognet if ((error = copyin(args, &ua, sizeof(ua))) != 0) 112135642Scognet return (error); 113135642Scognet 114283947Sian if (ua.len == 0) { 115283947Sian td->td_retval[0] = 0; 116283947Sian return (0); 117283947Sian } 118283947Sian 119283947Sian /* 120283947Sian * Validate arguments. Address and length are unsigned, 121283947Sian * so we can use wrapped overflow check. 122283947Sian */ 123283947Sian if (((ua.addr + ua.len) < ua.addr) || 124283947Sian ((ua.addr + ua.len) > VM_MAXUSER_ADDRESS)) { 125283947Sian ksiginfo_init_trap(&ksi); 126283947Sian ksi.ksi_signo = SIGSEGV; 127283947Sian ksi.ksi_code = SEGV_ACCERR; 128283947Sian ksi.ksi_addr = (void *)max(ua.addr, VM_MAXUSER_ADDRESS); 129283947Sian trapsignal(td, &ksi); 130283947Sian return (EINVAL); 131283947Sian } 132283947Sian 133283947Sian#if __ARM_ARCH >= 6 134283947Sian rv = sync_icache(ua.addr, ua.len); 135283947Sian if (rv != 1) { 136283947Sian ksiginfo_init_trap(&ksi); 137283947Sian ksi.ksi_signo = SIGSEGV; 138283947Sian ksi.ksi_code = SEGV_MAPERR; 139283947Sian ksi.ksi_addr = (void *)rv; 140283947Sian trapsignal(td, &ksi); 141283947Sian return (EINVAL); 142283947Sian } 143283947Sian#else 144135642Scognet cpu_icache_sync_range(ua.addr, ua.len); 145283947Sian#endif 146135642Scognet 147135642Scognet td->td_retval[0] = 0; 148166695Skevlo return (0); 149135642Scognet} 150135642Scognet 151135642Scognetstatic int 152135642Scognetarm32_drain_writebuf(struct thread *td, void *args) 153135642Scognet{ 154135642Scognet /* No args. */ 155135642Scognet 156295319Smmel#if __ARM_ARCH < 6 157295319Smmel cpu_drain_writebuf(); 158295319Smmel#else 159295319Smmel dsb(); 160295319Smmel cpu_l2cache_drain_writebuf(); 161295319Smmel#endif 162135642Scognet td->td_retval[0] = 0; 163166695Skevlo return (0); 164135642Scognet} 165135642Scognet 166142519Scognetstatic int 167142519Scognetarm32_set_tp(struct thread *td, void *args) 168142519Scognet{ 169142519Scognet 170284115Sandrew#if __ARM_ARCH >= 6 171280402Sian set_tls(args); 172239268Sgonzo#else 173307136Sed td->td_md.md_tp = (register_t)args; 174280402Sian *(register_t *)ARM_TP_ADDRESS = (register_t)args; 175239268Sgonzo#endif 176142519Scognet return (0); 177142519Scognet} 178142519Scognet 179142519Scognetstatic int 180142519Scognetarm32_get_tp(struct thread *td, void *args) 181142519Scognet{ 182142519Scognet 183284115Sandrew#if __ARM_ARCH >= 6 184307136Sed td->td_retval[0] = (register_t)get_tls(); 185239268Sgonzo#else 186280402Sian td->td_retval[0] = *(register_t *)ARM_TP_ADDRESS; 187239268Sgonzo#endif 188142519Scognet return (0); 189142519Scognet} 190142519Scognet 191129198Scognetint 192331643Sdimsysarch(struct thread *td, struct sysarch_args *uap) 193129198Scognet{ 194135642Scognet int error; 195135642Scognet 196223668Sjonathan#ifdef CAPABILITY_MODE 197219134Srwatson /* 198223692Sjonathan * When adding new operations, add a new case statement here to 199223692Sjonathan * explicitly indicate whether or not the operation is safe to 200223692Sjonathan * perform in capability mode. 201219134Srwatson */ 202219134Srwatson if (IN_CAPABILITY_MODE(td)) { 203219134Srwatson switch (uap->op) { 204223692Sjonathan case ARM_SYNC_ICACHE: 205223692Sjonathan case ARM_DRAIN_WRITEBUF: 206223692Sjonathan case ARM_SET_TP: 207223692Sjonathan case ARM_GET_TP: 208325307Smmel case ARM_GET_VFPSTATE: 209223692Sjonathan break; 210219134Srwatson 211223692Sjonathan default: 212226498Sdes#ifdef KTRACE 213226498Sdes if (KTRPOINT(td, KTR_CAPFAIL)) 214255677Spjd ktrcapfail(CAPFAIL_SYSCALL, NULL, NULL); 215226498Sdes#endif 216223692Sjonathan return (ECAPMODE); 217219134Srwatson } 218219134Srwatson } 219219134Srwatson#endif 220219134Srwatson 221135642Scognet switch (uap->op) { 222236991Simp case ARM_SYNC_ICACHE: 223135642Scognet error = arm32_sync_icache(td, uap->parms); 224135642Scognet break; 225236991Simp case ARM_DRAIN_WRITEBUF: 226135642Scognet error = arm32_drain_writebuf(td, uap->parms); 227135642Scognet break; 228142519Scognet case ARM_SET_TP: 229142519Scognet error = arm32_set_tp(td, uap->parms); 230142519Scognet break; 231142519Scognet case ARM_GET_TP: 232142519Scognet error = arm32_get_tp(td, uap->parms); 233142519Scognet break; 234325307Smmel case ARM_GET_VFPSTATE: 235325307Smmel error = arm_get_vfpstate(td, uap->parms); 236325307Smmel break; 237135642Scognet default: 238135642Scognet error = EINVAL; 239135642Scognet break; 240135642Scognet } 241135642Scognet return (error); 242129198Scognet} 243