Home | History | Annotate | Line # | Download | only in powerpc
      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
     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
    189 cpu_lwp_free(struct lwp *l, int proc)
    190 {
    191 
    192 	(void)l;
    193 }
    194 
    195 void
    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
    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
    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
    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
    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 *
    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
    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