1129198Scognet/*-
2129198Scognet * Copyright (c) 1982, 1986 The Regents of the University of California.
3129198Scognet * Copyright (c) 1989, 1990 William Jolitz
4129198Scognet * Copyright (c) 1994 John Dyson
5129198Scognet * All rights reserved.
6129198Scognet *
7129198Scognet * This code is derived from software contributed to Berkeley by
8129198Scognet * the Systems Programming Group of the University of Utah Computer
9129198Scognet * Science Department, and William Jolitz.
10129198Scognet *
11150868Scognet * Redistribution and use in source and binary :forms, with or without
12129198Scognet * modification, are permitted provided that the following conditions
13129198Scognet * are met:
14129198Scognet * 1. Redistributions of source code must retain the above copyright
15129198Scognet *    notice, this list of conditions and the following disclaimer.
16129198Scognet * 2. Redistributions in binary form must reproduce the above copyright
17129198Scognet *    notice, this list of conditions and the following disclaimer in the
18129198Scognet *    documentation and/or other materials provided with the distribution.
19129198Scognet * 3. All advertising materials mentioning features or use of this software
20129198Scognet *    must display the following acknowledgement:
21129198Scognet *	This product includes software developed by the University of
22129198Scognet *	California, Berkeley and its contributors.
23129198Scognet * 4. Neither the name of the University nor the names of its contributors
24129198Scognet *    may be used to endorse or promote products derived from this software
25129198Scognet *    without specific prior written permission.
26129198Scognet *
27129198Scognet * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28129198Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29129198Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30129198Scognet * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31129198Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32129198Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33129198Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37129198Scognet * SUCH DAMAGE.
38129198Scognet *
39129198Scognet *	from: @(#)vm_machdep.c	7.3 (Berkeley) 5/13/91
40129198Scognet *	Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
41129198Scognet */
42129198Scognet
43296861Sbz#include "opt_compat.h"
44296861Sbz
45129198Scognet#include <sys/cdefs.h>
46129198Scognet__FBSDID("$FreeBSD: stable/11/sys/arm/arm/vm_machdep.c 331722 2018-03-29 02:50:57Z eadler $");
47129198Scognet
48129198Scognet#include <sys/param.h>
49129198Scognet#include <sys/systm.h>
50129198Scognet#include <sys/kernel.h>
51129198Scognet#include <sys/malloc.h>
52129198Scognet#include <sys/mbuf.h>
53129198Scognet#include <sys/proc.h>
54129198Scognet#include <sys/socketvar.h>
55199135Skib#include <sys/syscall.h>
56255786Sglebius#include <sys/sysctl.h>
57199135Skib#include <sys/sysent.h>
58146599Scognet#include <sys/unistd.h>
59284115Sandrew
60129198Scognet#include <machine/cpu.h>
61257200Sian#include <machine/frame.h>
62129198Scognet#include <machine/pcb.h>
63145433Sdavidxu#include <machine/sysarch.h>
64129198Scognet#include <sys/lock.h>
65129198Scognet#include <sys/mutex.h>
66129198Scognet
67129198Scognet#include <vm/vm.h>
68169900Scognet#include <vm/pmap.h>
69129198Scognet#include <vm/vm_extern.h>
70129198Scognet#include <vm/vm_kern.h>
71129198Scognet#include <vm/vm_page.h>
72129198Scognet#include <vm/vm_map.h>
73129198Scognet#include <vm/vm_param.h>
74161105Scognet#include <vm/vm_pageout.h>
75147114Scognet#include <vm/uma.h>
76147114Scognet#include <vm/uma_int.h>
77129198Scognet
78166063Scognet#include <machine/md_var.h>
79263913Sandrew#include <machine/vfp.h>
80166063Scognet
81247864Sandrew/*
82253968Sandrew * struct switchframe and trapframe must both be a multiple of 8
83253968Sandrew * for correct stack alignment.
84247864Sandrew */
85307136Sed_Static_assert((sizeof(struct switchframe) % 8) == 0, "Bad alignment");
86307136Sed_Static_assert((sizeof(struct trapframe) % 8) == 0, "Bad alignment");
87247864Sandrew
88288983Skibuint32_t initial_fpscr = VFPSCR_DN | VFPSCR_FZ;
89288983Skib
90129198Scognet/*
91129198Scognet * Finish a fork operation, with process p2 nearly set up.
92129198Scognet * Copy and update the pcb, set up the stack so that the child
93129198Scognet * ready to run and return to user mode.
94129198Scognet */
95129198Scognetvoid
96331643Sdimcpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
97129198Scognet{
98188019Scognet	struct pcb *pcb2;
99129198Scognet	struct trapframe *tf;
100129198Scognet	struct mdproc *mdp2;
101129198Scognet
102146599Scognet	if ((flags & RFPROC) == 0)
103146599Scognet		return;
104276190Sian
105276190Sian	/* Point the pcb to the top of the stack */
106276190Sian	pcb2 = (struct pcb *)
107276190Sian	    (td2->td_kstack + td2->td_kstack_pages * PAGE_SIZE) - 1;
108135657Scognet#ifdef __XSCALE__
109171622Scognet#ifndef CPU_XSCALE_CORE3
110135657Scognet	pmap_use_minicache(td2->td_kstack, td2->td_kstack_pages * PAGE_SIZE);
111135657Scognet#endif
112171622Scognet#endif
113317005Smmel#ifdef VFP
114317005Smmel	/* Store actual state of VFP */
115317005Smmel	if (curthread == td1) {
116317005Smmel		critical_enter();
117317005Smmel		vfp_store(&td1->td_pcb->pcb_vfpstate, false);
118317005Smmel		critical_exit();
119317005Smmel	}
120317005Smmel#endif
121129198Scognet	td2->td_pcb = pcb2;
122283366Sandrew
123276190Sian	/* Clone td1's pcb */
124129198Scognet	bcopy(td1->td_pcb, pcb2, sizeof(*pcb2));
125283366Sandrew
126276190Sian	/* Point to mdproc and then copy over td1's contents */
127129198Scognet	mdp2 = &p2->p_md;
128129198Scognet	bcopy(&td1->td_proc->p_md, mdp2, sizeof(*mdp2));
129276190Sian
130276190Sian	/* Point the frame to the stack in front of pcb and copy td1's frame */
131276190Sian	td2->td_frame = (struct trapframe *)pcb2 - 1;
132276190Sian	*td2->td_frame = *td1->td_frame;
133276190Sian
134276190Sian	/*
135276190Sian	 * Create a new fresh stack for the new process.
136276190Sian	 * Copy the trap frame for the return to user mode as if from a
137276190Sian	 * syscall.  This copies most of the user mode register values.
138276190Sian	 */
139276190Sian	pmap_set_pcb_pagedir(vmspace_pmap(p2->p_vmspace), pcb2);
140276190Sian	pcb2->pcb_regs.sf_r4 = (register_t)fork_return;
141276190Sian	pcb2->pcb_regs.sf_r5 = (register_t)td2;
142276190Sian	pcb2->pcb_regs.sf_lr = (register_t)fork_trampoline;
143276190Sian	pcb2->pcb_regs.sf_sp = STACKALIGN(td2->td_frame);
144307136Sed#if __ARM_ARCH >= 6
145307136Sed	pcb2->pcb_regs.sf_tpidrurw = (register_t)get_tls();
146307136Sed#endif
147276190Sian
148262949Sian	pcb2->pcb_vfpcpu = -1;
149288983Skib	pcb2->pcb_vfpstate.fpscr = initial_fpscr;
150283366Sandrew
151276190Sian	tf = td2->td_frame;
152271398Sandrew	tf->tf_spsr &= ~PSR_C;
153129198Scognet	tf->tf_r0 = 0;
154135657Scognet	tf->tf_r1 = 0;
155144637Sjhb
156276190Sian
157170305Sjeff	/* Setup to release spin count in fork_exit(). */
158144637Sjhb	td2->td_md.md_spinlock_count = 1;
159297793Spfg	td2->td_md.md_saved_cspr = PSR_SVC32_MODE;
160307136Sed#if __ARM_ARCH < 6
161218310Simp	td2->td_md.md_tp = *(register_t *)ARM_TP_ADDRESS;
162239268Sgonzo#endif
163129198Scognet}
164283366Sandrew
165129198Scognetvoid
166129198Scognetcpu_thread_swapin(struct thread *td)
167129198Scognet{
168236991Simp}
169129198Scognet
170236991Simpvoid
171129198Scognetcpu_thread_swapout(struct thread *td)
172236991Simp{
173129198Scognet}
174129198Scognet
175129198Scognetvoid
176199135Skibcpu_set_syscall_retval(struct thread *td, int error)
177199135Skib{
178257217Sian	struct trapframe *frame;
179199135Skib	int fixup;
180199135Skib#ifdef __ARMEB__
181261564Sandrew	u_int call;
182199135Skib#endif
183199135Skib
184199135Skib	frame = td->td_frame;
185199135Skib	fixup = 0;
186199135Skib
187199135Skib#ifdef __ARMEB__
188261564Sandrew	/*
189261564Sandrew	 * __syscall returns an off_t while most other syscalls return an
190261564Sandrew	 * int. As an off_t is 64-bits and an int is 32-bits we need to
191296861Sbz	 * place the returned data into r1. As the lseek and freebsd6_lseek
192261564Sandrew	 * syscalls also return an off_t they do not need this fixup.
193261564Sandrew	 */
194261564Sandrew	call = frame->tf_r7;
195261564Sandrew	if (call == SYS___syscall) {
196199135Skib		register_t *ap = &frame->tf_r0;
197199135Skib		register_t code = ap[_QUAD_LOWWORD];
198199135Skib		if (td->td_proc->p_sysent->sv_mask)
199199135Skib			code &= td->td_proc->p_sysent->sv_mask;
200296948Semaste		fixup = (code != SYS_lseek);
201199135Skib	}
202199135Skib#endif
203199135Skib
204199135Skib	switch (error) {
205199135Skib	case 0:
206199135Skib		if (fixup) {
207199135Skib			frame->tf_r0 = 0;
208199135Skib			frame->tf_r1 = td->td_retval[0];
209199135Skib		} else {
210199135Skib			frame->tf_r0 = td->td_retval[0];
211199135Skib			frame->tf_r1 = td->td_retval[1];
212199135Skib		}
213271398Sandrew		frame->tf_spsr &= ~PSR_C;   /* carry bit */
214199135Skib		break;
215199135Skib	case ERESTART:
216199135Skib		/*
217199135Skib		 * Reconstruct the pc to point at the swi.
218199135Skib		 */
219282779Sandrew#if __ARM_ARCH >= 7
220282779Sandrew		if ((frame->tf_spsr & PSR_T) != 0)
221282779Sandrew			frame->tf_pc -= THUMB_INSN_SIZE;
222282779Sandrew		else
223282779Sandrew#endif
224282779Sandrew			frame->tf_pc -= INSN_SIZE;
225199135Skib		break;
226199135Skib	case EJUSTRETURN:
227199135Skib		/* nothing to do */
228199135Skib		break;
229199135Skib	default:
230199135Skib		frame->tf_r0 = error;
231271398Sandrew		frame->tf_spsr |= PSR_C;    /* carry bit */
232199135Skib		break;
233199135Skib	}
234199135Skib}
235199135Skib
236129198Scognet/*
237301961Skib * Initialize machine state, mostly pcb and trap frame for a new
238301961Skib * thread, about to return to userspace.  Put enough state in the new
239301961Skib * thread's PCB to get it to go back to the fork_return(), which
240301961Skib * finalizes the thread state and handles peculiarities of the first
241301961Skib * return to userspace for the new thread.
242129198Scognet */
243129198Scognetvoid
244301961Skibcpu_copy_thread(struct thread *td, struct thread *td0)
245129198Scognet{
246137214Scognet
247137214Scognet	bcopy(td0->td_frame, td->td_frame, sizeof(struct trapframe));
248137214Scognet	bcopy(td0->td_pcb, td->td_pcb, sizeof(struct pcb));
249144637Sjhb
250276190Sian	td->td_pcb->pcb_regs.sf_r4 = (register_t)fork_return;
251276190Sian	td->td_pcb->pcb_regs.sf_r5 = (register_t)td;
252276190Sian	td->td_pcb->pcb_regs.sf_lr = (register_t)fork_trampoline;
253276190Sian	td->td_pcb->pcb_regs.sf_sp = STACKALIGN(td->td_frame);
254276190Sian
255276190Sian	td->td_frame->tf_spsr &= ~PSR_C;
256276190Sian	td->td_frame->tf_r0 = 0;
257276190Sian
258170305Sjeff	/* Setup to release spin count in fork_exit(). */
259144637Sjhb	td->td_md.md_spinlock_count = 1;
260276190Sian	td->td_md.md_saved_cspr = PSR_SVC32_MODE;
261129198Scognet}
262129198Scognet
263129198Scognet/*
264301961Skib * Set that machine state for performing an upcall that starts
265301961Skib * the entry function with the given argument.
266129198Scognet */
267129198Scognetvoid
268301961Skibcpu_set_upcall(struct thread *td, void (*entry)(void *), void *arg,
269145433Sdavidxu	stack_t *stack)
270129198Scognet{
271137214Scognet	struct trapframe *tf = td->td_frame;
272137214Scognet
273276190Sian	tf->tf_usr_sp = STACKALIGN((int)stack->ss_sp + stack->ss_size);
274145433Sdavidxu	tf->tf_pc = (int)entry;
275145433Sdavidxu	tf->tf_r0 = (int)arg;
276137214Scognet	tf->tf_spsr = PSR_USR32_MODE;
277129198Scognet}
278129198Scognet
279147889Sdavidxuint
280145433Sdavidxucpu_set_user_tls(struct thread *td, void *tls_base)
281145433Sdavidxu{
282145433Sdavidxu
283307136Sed#if __ARM_ARCH >= 6
284307136Sed	td->td_pcb->pcb_regs.sf_tpidrurw = (register_t)tls_base;
285307136Sed	if (td == curthread)
286307136Sed		set_tls(tls_base);
287307136Sed#else
288239268Sgonzo	td->td_md.md_tp = (register_t)tls_base;
289239268Sgonzo	if (td == curthread) {
290145433Sdavidxu		critical_enter();
291218310Simp		*(register_t *)ARM_TP_ADDRESS = (register_t)tls_base;
292145433Sdavidxu		critical_exit();
293145433Sdavidxu	}
294307136Sed#endif
295147889Sdavidxu	return (0);
296145433Sdavidxu}
297145433Sdavidxu
298145433Sdavidxuvoid
299129198Scognetcpu_thread_exit(struct thread *td)
300129198Scognet{
301129198Scognet}
302129198Scognet
303129198Scognetvoid
304173615Smarcelcpu_thread_alloc(struct thread *td)
305129198Scognet{
306236991Simp	td->td_pcb = (struct pcb *)(td->td_kstack + td->td_kstack_pages *
307129198Scognet	    PAGE_SIZE) - 1;
308245942Sandrew	/*
309245942Sandrew	 * Ensure td_frame is aligned to an 8 byte boundary as it will be
310245942Sandrew	 * placed into the stack pointer which must be 8 byte aligned in
311245942Sandrew	 * the ARM EABI.
312245942Sandrew	 */
313276190Sian	td->td_frame = (struct trapframe *)((caddr_t)td->td_pcb) - 1;
314276190Sian
315137214Scognet#ifdef __XSCALE__
316171622Scognet#ifndef CPU_XSCALE_CORE3
317137214Scognet	pmap_use_minicache(td->td_kstack, td->td_kstack_pages * PAGE_SIZE);
318171622Scognet#endif
319236991Simp#endif
320129198Scognet}
321173615Smarcel
322129198Scognetvoid
323173615Smarcelcpu_thread_free(struct thread *td)
324173615Smarcel{
325173615Smarcel}
326173615Smarcel
327173615Smarcelvoid
328129198Scognetcpu_thread_clean(struct thread *td)
329129198Scognet{
330129198Scognet}
331129198Scognet
332129198Scognet/*
333129198Scognet * Intercept the return address from a freshly forked process that has NOT
334129198Scognet * been scheduled yet.
335129198Scognet *
336129198Scognet * This is needed to make kernel threads stay in kernel mode.
337129198Scognet */
338129198Scognetvoid
339301961Skibcpu_fork_kthread_handler(struct thread *td, void (*func)(void *), void *arg)
340129198Scognet{
341276190Sian	td->td_pcb->pcb_regs.sf_r4 = (register_t)func;	/* function */
342276190Sian	td->td_pcb->pcb_regs.sf_r5 = (register_t)arg;	/* first arg */
343129198Scognet}
344129198Scognet
345129198Scognet/*
346129198Scognet * Software interrupt handler for queued VM system processing.
347236991Simp */
348236991Simpvoid
349129198Scognetswi_vm(void *dummy)
350129198Scognet{
351283366Sandrew
352166063Scognet	if (busdma_swi_pending)
353166063Scognet		busdma_swi();
354129198Scognet}
355129198Scognet
356129198Scognetvoid
357129198Scognetcpu_exit(struct thread *td)
358129198Scognet{
359129198Scognet}
360147114Scognet
361