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