1 /* $NetBSD: vm_machdep.c,v 1.105 2022/12/05 16:01:03 martin Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
5 * Copyright (C) 1995, 1996 TooLs GmbH.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by TooLs GmbH.
19 * 4. The name of TooLs GmbH may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.105 2022/12/05 16:01:03 martin Exp $");
36
37 #ifdef _KERNEL_OPT
38 #include "opt_altivec.h"
39 #include "opt_ppcarch.h"
40 #include "opt_ppccache.h"
41 #endif
42
43 #include <sys/param.h>
44 #include <sys/core.h>
45 #include <sys/exec.h>
46 #include <sys/proc.h>
47 #include <sys/systm.h>
48 #include <sys/vnode.h>
49 #include <sys/buf.h>
50
51 #include <uvm/uvm.h>
52
53 #if defined(ALTIVEC) || defined(PPC_HAVE_SPE)
54 #include <powerpc/altivec.h>
55 #endif
56 #include <machine/fpu.h>
57 #include <machine/pcb.h>
58 #include <machine/psl.h>
59
60 #ifdef PPC_IBM4XX
61 vaddr_t vmaprange(struct proc *, vaddr_t, vsize_t, int);
62 void vunmaprange(vaddr_t, vsize_t);
63 #endif
64
65 void cpu_lwp_bootstrap(void);
66
67 /*
68 * Finish a fork operation, with execution context l2 nearly set up.
69 * Copy and update the pcb and trap frame, making the child ready to run.
70 *
71 * Rig the child's kernel stack so that it will have a switch frame which
72 * returns to cpu_lwp_bootstrap() which will call child_return() with l2
73 * as its argument. This causes the newly-created child process to go
74 * directly to user level with an apparent return value of 0 from
75 * fork(), while the parent process returns normally.
76 *
77 * l1 is the execution context being forked; if l1 == &lwp0, we are creating
78 * a kernel thread, and the return path and argument are specified with
79 * `func' and `arg'.
80 *
81 * If an alternate user-level stack is requested (with non-zero values
82 * in both the stack and stacksize args), set up the user stack pointer
83 * accordingly.
84 */
85 void
cpu_lwp_fork(struct lwp * l1,struct lwp * l2,void * stack,size_t stacksize,void (* func)(void *),void * arg)86 cpu_lwp_fork(struct lwp *l1, struct lwp *l2, void *stack, size_t stacksize,
87 void (*func)(void *), void *arg)
88 {
89
90 /*
91 * If l1 != curlwp && l1 == &lwp0, we're creating a kernel thread.
92 */
93 KASSERT(l1 == curlwp || l1 == &lwp0);
94
95 struct pcb * const pcb1 = lwp_getpcb(l1);
96 struct pcb * const pcb2 = lwp_getpcb(l2);
97
98 /* Set up user trapframe pointer. */
99 l2->l_md.md_utf = trapframe(l2);
100
101 /* Copy PCB. */
102 *pcb2 = *pcb1;
103
104 pcb2->pcb_pm = l2->l_proc->p_vmspace->vm_map.pmap;
105
106 /*
107 * Setup the trap frame for the new process
108 */
109 *l2->l_md.md_utf = *l1->l_md.md_utf;
110
111 /*
112 * If specified, give the child a different stack. Make sure to
113 * reserve enough at the top to store the previous LR.
114 */
115 if (stack != NULL) {
116 l2->l_md.md_utf->tf_fixreg[1] =
117 ((register_t)stack + stacksize - STACK_ALIGNBYTES)
118 & ~STACK_ALIGNBYTES;
119 }
120
121 /*
122 * Now deal setting up the initial function and its argument.
123 */
124 struct ktrapframe * const ktf = ktrapframe(l2);
125 struct callframe * const cf = ((struct callframe *)ktf) - 1;
126 struct switchframe * const sf = ((struct switchframe *)cf) - 1;
127
128 /*
129 * Align stack pointer
130 * struct ktrapframe has a partial callframe (sp & lr)
131 * followed by a real trapframe. The partial callframe
132 * is for the callee to store LR. The SP isn't really used
133 * since trap/syscall will use the SP in the trapframe.
134 * There happens to be a partial callframe in front of the
135 * trapframe, too.
136 */
137 ktf->ktf_lr = (register_t) cpu_lwp_bootstrap;
138 ktf->ktf_sp = (register_t) (ktf + 1); /* just in case */
139
140 cf->cf_sp = (register_t) ktf;
141 cf->cf_r31 = (register_t) func;
142 cf->cf_r30 = (register_t) arg;
143
144 memset((void *)sf, 0, sizeof *sf); /* just in case */
145 sf->sf_sp = (register_t) cf;
146 #if defined (PPC_OEA) || defined (PPC_OEA64_BRIDGE)
147 sf->sf_user_sr = pmap_kernel()->pm_sr[USER_SR]; /* again, just in case */
148 #endif
149 pcb2->pcb_sp = (register_t)sf;
150 pcb2->pcb_kmapsr = 0;
151 pcb2->pcb_umapsr = 0;
152 #ifdef PPC_HAVE_FPU
153 pcb2->pcb_flags = PSL_FE_DFLT;
154 #endif
155 #ifdef CACHE_PROTO_MEI
156 {
157 paddr_t pa;
158 int dcache_line_size, i;
159
160 /* Flush on cache values for other cpu. */
161
162 dcache_line_size = curcpu()->ci_ci.dcache_line_size;
163 pa = vtophys((vaddr_t)sf);
164 for (i = 0; i < SFRAMELEN + CALLFRAMELEN + FRAMELEN;
165 i += dcache_line_size) {
166 __asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
167 pa += dcache_line_size;
168 }
169 __asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
170 pa = vtophys((vaddr_t)pcb2->pcb_pm);
171 for (i = 0; i < sizeof(*pcb2->pcb_pm); i += dcache_line_size) {
172 __asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
173 pa += dcache_line_size;
174 }
175 __asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
176 pa = vtophys((vaddr_t)pcb2);
177 for (i = 0; i < sizeof(*pcb2); i += dcache_line_size) {
178 __asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
179 pa += dcache_line_size;
180 }
181 __asm volatile ("dcbf 0,%0"::"r"(pa):"memory");
182
183 /* Need more flush? */
184 }
185 #endif
186 }
187
188 void
cpu_lwp_free(struct lwp * l,int proc)189 cpu_lwp_free(struct lwp *l, int proc)
190 {
191
192 (void)l;
193 }
194
195 void
cpu_lwp_free2(struct lwp * l)196 cpu_lwp_free2(struct lwp *l)
197 {
198
199 (void)l;
200 }
201
202 #ifdef PPC_IBM4XX
203 /*
204 * Map a range of user addresses into the kernel.
205 */
206 vaddr_t
vmaprange(struct proc * p,vaddr_t uaddr,vsize_t len,int prot)207 vmaprange(struct proc *p, vaddr_t uaddr, vsize_t len, int prot)
208 {
209 vaddr_t faddr, taddr, kaddr;
210 vsize_t off;
211 paddr_t pa;
212
213 faddr = trunc_page(uaddr);
214 off = uaddr - faddr;
215 len = round_page(off + len);
216 taddr = uvm_km_alloc(phys_map, len, 0, UVM_KMF_VAONLY | UVM_KMF_WAITVA);
217 kaddr = taddr + off;
218 for (; len > 0; len -= PAGE_SIZE) {
219 (void) pmap_extract(vm_map_pmap(&p->p_vmspace->vm_map),
220 faddr, &pa);
221 pmap_kenter_pa(taddr, pa, prot, 0);
222 faddr += PAGE_SIZE;
223 taddr += PAGE_SIZE;
224 }
225 return (kaddr);
226 }
227
228 /*
229 * Undo vmaprange.
230 */
231 void
vunmaprange(vaddr_t kaddr,vsize_t len)232 vunmaprange(vaddr_t kaddr, vsize_t len)
233 {
234 vaddr_t addr;
235 vsize_t off;
236
237 addr = trunc_page(kaddr);
238 off = kaddr - addr;
239 len = round_page(off + len);
240 pmap_kremove(addr, len);
241 uvm_km_free(phys_map, addr, len, UVM_KMF_VAONLY);
242 }
243 #endif /* PPC_IBM4XX */
244
245 /*
246 * Map a user I/O request into kernel virtual address space.
247 * Note: these pages have already been locked by uvm_vslock.
248 */
249 int
vmapbuf(struct buf * bp,vsize_t len)250 vmapbuf(struct buf *bp, vsize_t len)
251 {
252 vaddr_t faddr, taddr;
253 vsize_t off;
254 paddr_t pa;
255 int prot = VM_PROT_READ | ((bp->b_flags & B_READ) ? VM_PROT_WRITE : 0);
256
257 #ifdef DIAGNOSTIC
258 if (!(bp->b_flags & B_PHYS))
259 panic("vmapbuf");
260 #endif
261 /*
262 * XXX Reimplement this with vmaprange (on at least PPC_IBM4XX CPUs).
263 */
264 bp->b_saveaddr = bp->b_data;
265 faddr = trunc_page((vaddr_t)bp->b_saveaddr);
266 off = (vaddr_t)bp->b_data - faddr;
267 len = round_page(off + len);
268 taddr = uvm_km_alloc(phys_map, len, 0, UVM_KMF_VAONLY | UVM_KMF_WAITVA);
269 bp->b_data = (void *)(taddr + off);
270 for (; len > 0; len -= PAGE_SIZE) {
271 (void) pmap_extract(vm_map_pmap(&bp->b_proc->p_vmspace->vm_map),
272 faddr, &pa);
273 /*
274 * Use pmap_enter so the referenced and modified bits are
275 * appropriately set.
276 */
277 pmap_kenter_pa(taddr, pa, prot, 0);
278 faddr += PAGE_SIZE;
279 taddr += PAGE_SIZE;
280 }
281 pmap_update(pmap_kernel());
282
283 return 0;
284 }
285
286 /*
287 * Unmap a previously-mapped user I/O request.
288 */
289 void
vunmapbuf(struct buf * bp,vsize_t len)290 vunmapbuf(struct buf *bp, vsize_t len)
291 {
292 vaddr_t addr;
293 vsize_t off;
294
295 #ifdef DIAGNOSTIC
296 if (!(bp->b_flags & B_PHYS))
297 panic("vunmapbuf");
298 #endif
299 addr = trunc_page((vaddr_t)bp->b_data);
300 off = (vaddr_t)bp->b_data - addr;
301 len = round_page(off + len);
302 /*
303 * Since the pages were entered by pmap_enter, use pmap_remove
304 * to remove them.
305 */
306 pmap_kremove(addr, len);
307 pmap_update(pmap_kernel());
308 uvm_km_free(phys_map, addr, len, UVM_KMF_VAONLY);
309 bp->b_data = bp->b_saveaddr;
310 bp->b_saveaddr = 0;
311 }
312
313 #ifdef __HAVE_CPU_UAREA_ROUTINES
314 void *
cpu_uarea_alloc(bool system)315 cpu_uarea_alloc(bool system)
316 {
317 #ifdef PMAP_MAP_POOLPAGE
318 struct pglist pglist;
319 int error;
320
321 /*
322 * Allocate a new physically contiguous uarea which can be
323 * direct-mapped.
324 */
325 error = uvm_pglistalloc(USPACE, 0, ~0UL, 0, 0, &pglist, 1, 1);
326 if (error) {
327 return NULL;
328 }
329
330 /*
331 * Get the physical address from the first page.
332 */
333 const struct vm_page * const pg = TAILQ_FIRST(&pglist);
334 KASSERT(pg != NULL);
335 const paddr_t pa = VM_PAGE_TO_PHYS(pg);
336
337 /*
338 * We need to return a direct-mapped VA for the pa.
339 */
340
341 return (void *)(uintptr_t)PMAP_MAP_POOLPAGE(pa);
342 #else
343 return NULL;
344 #endif
345 }
346
347 /*
348 * Return true if we freed it, false if we didn't.
349 */
350 bool
cpu_uarea_free(void * vva)351 cpu_uarea_free(void *vva)
352 {
353 #ifdef PMAP_UNMAP_POOLPAGE
354 vaddr_t va = (vaddr_t) vva;
355 if (va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS)
356 return false;
357
358 /*
359 * Since the pages are physically contiguous, the vm_page structure
360 * will be as well.
361 */
362 struct vm_page *pg = PHYS_TO_VM_PAGE(PMAP_UNMAP_POOLPAGE(va));
363 KASSERT(pg != NULL);
364 for (size_t i = 0; i < UPAGES; i++, pg++) {
365 uvm_pagefree(pg);
366 }
367 return true;
368 #else
369 return false;
370 #endif
371 }
372 #endif /* __HAVE_CPU_UAREA_ROUTINES */
373