1 /*        $NetBSD: vm_machdep.c,v 1.106 2023/12/15 09:43:59 rin 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.106 2023/12/15 09:43:59 rin 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, PMAP_DIRECT_MAPPED_LEN, 0, 0,
326               &pglist, 1, 1);
327           if (error) {
328                     return NULL;
329           }
330 
331           /*
332            * Get the physical address from the first page.
333            */
334           const struct vm_page * const pg = TAILQ_FIRST(&pglist);
335           KASSERT(pg != NULL);
336           const paddr_t pa = VM_PAGE_TO_PHYS(pg);
337 
338           /*
339            * We need to return a direct-mapped VA for the pa.
340            */
341 
342           return (void *)(uintptr_t)PMAP_MAP_POOLPAGE(pa);
343 #else
344           return NULL;
345 #endif
346 }
347 
348 /*
349  * Return true if we freed it, false if we didn't.
350  */
351 bool
cpu_uarea_free(void * vva)352 cpu_uarea_free(void *vva)
353 {
354 #ifdef PMAP_UNMAP_POOLPAGE
355           vaddr_t va = (vaddr_t) vva;
356           if (va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS)
357                     return false;
358 
359           /*
360            * Since the pages are physically contiguous, the vm_page structure
361            * will be as well.
362            */
363           struct vm_page *pg = PHYS_TO_VM_PAGE(PMAP_UNMAP_POOLPAGE(va));
364           KASSERT(pg != NULL);
365           for (size_t i = 0; i < UPAGES; i++, pg++) {
366                     uvm_pagefree(pg);
367           }
368           return true;
369 #else
370           return false;
371 #endif
372 }
373 #endif /* __HAVE_CPU_UAREA_ROUTINES */
374