sys_machdep.c revision 307136
1228753Smm/*-
2228753Smm * Copyright (c) 1990 The Regents of the University of California.
3228753Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm * 3. Neither the name of the University nor the names of its contributors
14228753Smm *    may be used to endorse or promote products derived from this software
15228753Smm *    without specific prior written permission.
16228753Smm *
17228753Smm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18228753Smm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19228753Smm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20228753Smm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21228753Smm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22228753Smm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23228753Smm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24228753Smm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25228753Smm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26228753Smm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27228753Smm * SUCH DAMAGE.
28228753Smm *
29229592Smm *	from: @(#)sys_machdep.c	5.5 (Berkeley) 1/19/91
30228753Smm */
31228753Smm
32228753Smm#include <sys/cdefs.h>
33228753Smm__FBSDID("$FreeBSD: stable/11/sys/arm/arm/sys_machdep.c 307136 2016-10-12 09:17:41Z ed $");
34228753Smm
35228753Smm#include "opt_capsicum.h"
36228753Smm
37228753Smm#include <sys/param.h>
38228753Smm#include <sys/systm.h>
39228753Smm#include <sys/capsicum.h>
40228753Smm#include <sys/proc.h>
41228753Smm#include <sys/sysproto.h>
42228753Smm#include <sys/syscall.h>
43228753Smm#include <sys/sysent.h>
44228753Smm#include <vm/vm.h>
45228753Smm#include <vm/vm_extern.h>
46228753Smm
47228753Smm#include <machine/cpu.h>
48228753Smm#include <machine/sysarch.h>
49228753Smm#include <machine/vmparam.h>
50228753Smm
51228753Smm#ifndef _SYS_SYSPROTO_H_
52228753Smmstruct sysarch_args {
53228753Smm	int op;
54228753Smm	char *parms;
55228753Smm};
56228753Smm#endif
57228753Smm
58228753Smm/* Prototypes */
59228753Smmstatic int arm32_sync_icache (struct thread *, void *);
60228753Smmstatic int arm32_drain_writebuf(struct thread *, void *);
61228753Smm
62228753Smm#if __ARM_ARCH >= 6
63228753Smmstatic int
64228753Smmsync_icache(uintptr_t addr, size_t len)
65228753Smm{
66228753Smm	size_t size;
67228753Smm	vm_offset_t rv;
68228753Smm
69228753Smm	/*
70228753Smm	 * Align starting address to even number because value of "1"
71228753Smm	 * is used as return value for success.
72228753Smm	 */
73228753Smm	len += addr & 1;
74228753Smm	addr &= ~1;
75228753Smm
76228753Smm	/* Break whole range to pages. */
77228753Smm	do {
78228753Smm		size = PAGE_SIZE - (addr & PAGE_MASK);
79228753Smm		size = min(size, len);
80228753Smm		rv = dcache_wb_pou_checked(addr, size);
81228753Smm		if (rv == 1) /* see dcache_wb_pou_checked() */
82228753Smm			rv = icache_inv_pou_checked(addr, size);
83228753Smm		if (rv != 1) {
84228753Smm			if (!useracc((void *)addr, size, VM_PROT_READ)) {
85228753Smm				/* Invalid access */
86228753Smm				return (rv);
87228753Smm			}
88228753Smm			/* Valid but unmapped page - skip it. */
89228753Smm		}
90228753Smm		len -= size;
91228753Smm		addr += size;
92228753Smm	} while (len > 0);
93228753Smm
94228753Smm	/* Invalidate branch predictor buffer. */
95228753Smm	bpb_inv_all();
96228753Smm	return (1);
97228753Smm}
98228753Smm#endif
99228753Smm
100228753Smmstatic int
101228753Smmarm32_sync_icache(struct thread *td, void *args)
102228753Smm{
103228753Smm	struct arm_sync_icache_args ua;
104228753Smm	int error;
105228753Smm	ksiginfo_t ksi;
106228753Smm#if __ARM_ARCH >= 6
107228753Smm	vm_offset_t rv;
108228753Smm#endif
109228753Smm
110228753Smm	if ((error = copyin(args, &ua, sizeof(ua))) != 0)
111228753Smm		return (error);
112228753Smm
113228753Smm	if  (ua.len == 0) {
114228753Smm		td->td_retval[0] = 0;
115228753Smm		return (0);
116228753Smm	}
117228753Smm
118228753Smm	/*
119228753Smm	 * Validate arguments. Address and length are unsigned,
120228753Smm	 * so we can use wrapped overflow check.
121228753Smm	 */
122228753Smm	if (((ua.addr + ua.len) < ua.addr) ||
123228753Smm	    ((ua.addr + ua.len) > VM_MAXUSER_ADDRESS)) {
124228753Smm		ksiginfo_init_trap(&ksi);
125228753Smm		ksi.ksi_signo = SIGSEGV;
126228753Smm		ksi.ksi_code = SEGV_ACCERR;
127228753Smm		ksi.ksi_addr = (void *)max(ua.addr, VM_MAXUSER_ADDRESS);
128228753Smm		trapsignal(td, &ksi);
129228753Smm		return (EINVAL);
130228753Smm	}
131228753Smm
132228753Smm#if __ARM_ARCH >= 6
133228753Smm	rv = sync_icache(ua.addr, ua.len);
134228753Smm	if (rv != 1) {
135228753Smm		ksiginfo_init_trap(&ksi);
136228753Smm		ksi.ksi_signo = SIGSEGV;
137228753Smm		ksi.ksi_code = SEGV_MAPERR;
138228753Smm		ksi.ksi_addr = (void *)rv;
139228753Smm		trapsignal(td, &ksi);
140228753Smm		return (EINVAL);
141228753Smm	}
142228753Smm#else
143228753Smm	cpu_icache_sync_range(ua.addr, ua.len);
144228753Smm#endif
145228753Smm
146228753Smm	td->td_retval[0] = 0;
147228753Smm	return (0);
148228753Smm}
149228753Smm
150228753Smmstatic int
151228753Smmarm32_drain_writebuf(struct thread *td, void *args)
152228753Smm{
153228753Smm	/* No args. */
154228753Smm
155228753Smm#if __ARM_ARCH < 6
156228753Smm	cpu_drain_writebuf();
157228753Smm#else
158228753Smm	dsb();
159228753Smm	cpu_l2cache_drain_writebuf();
160228753Smm#endif
161228753Smm	td->td_retval[0] = 0;
162228753Smm	return (0);
163228753Smm}
164228753Smm
165228753Smmstatic int
166228753Smmarm32_set_tp(struct thread *td, void *args)
167228753Smm{
168228753Smm
169228753Smm#if __ARM_ARCH >= 6
170228753Smm	set_tls(args);
171228753Smm#else
172228753Smm	td->td_md.md_tp = (register_t)args;
173228753Smm	*(register_t *)ARM_TP_ADDRESS = (register_t)args;
174228753Smm#endif
175228753Smm	return (0);
176228753Smm}
177228753Smm
178228753Smmstatic int
179228753Smmarm32_get_tp(struct thread *td, void *args)
180228753Smm{
181228753Smm
182228753Smm#if __ARM_ARCH >= 6
183228753Smm	td->td_retval[0] = (register_t)get_tls();
184228753Smm#else
185228753Smm	td->td_retval[0] = *(register_t *)ARM_TP_ADDRESS;
186228753Smm#endif
187228753Smm	return (0);
188228753Smm}
189228753Smm
190228753Smmint
191228753Smmsysarch(td, uap)
192228753Smm	struct thread *td;
193228753Smm	register struct sysarch_args *uap;
194228753Smm{
195228753Smm	int error;
196228753Smm
197228753Smm#ifdef CAPABILITY_MODE
198228753Smm	/*
199228753Smm	 * When adding new operations, add a new case statement here to
200228753Smm	 * explicitly indicate whether or not the operation is safe to
201228753Smm	 * perform in capability mode.
202228753Smm	 */
203228753Smm	if (IN_CAPABILITY_MODE(td)) {
204228753Smm		switch (uap->op) {
205228753Smm		case ARM_SYNC_ICACHE:
206228753Smm		case ARM_DRAIN_WRITEBUF:
207228753Smm		case ARM_SET_TP:
208228753Smm		case ARM_GET_TP:
209228753Smm			break;
210228753Smm
211228753Smm		default:
212228753Smm#ifdef KTRACE
213228753Smm			if (KTRPOINT(td, KTR_CAPFAIL))
214228753Smm				ktrcapfail(CAPFAIL_SYSCALL, NULL, NULL);
215228753Smm#endif
216228753Smm			return (ECAPMODE);
217228753Smm		}
218228753Smm	}
219228753Smm#endif
220228753Smm
221228753Smm	switch (uap->op) {
222228753Smm	case ARM_SYNC_ICACHE:
223228753Smm		error = arm32_sync_icache(td, uap->parms);
224228753Smm		break;
225228753Smm	case ARM_DRAIN_WRITEBUF:
226228753Smm		error = arm32_drain_writebuf(td, uap->parms);
227228753Smm		break;
228228753Smm	case ARM_SET_TP:
229228753Smm		error = arm32_set_tp(td, uap->parms);
230228753Smm		break;
231228753Smm	case ARM_GET_TP:
232228753Smm		error = arm32_get_tp(td, uap->parms);
233228753Smm		break;
234228753Smm	default:
235228753Smm		error = EINVAL;
236228753Smm		break;
237228753Smm	}
238228753Smm	return (error);
239228753Smm}
240228753Smm