Edit

IABSD.fr/src/sys/uvm/uvm_glue.c

Branch :

  • Show log

    Commit

  • Author : deraadt
    Date : 2026-02-11 22:34:40
    Hash : 012cf974
    Message : Annotate some more uvmexp.h field as [a]tomic and only manipulate them via the atomic macros. In the near future we'll use these fields in the pagedaemon to observe live system state and make better decisions. discussed with beck and others, ok bluhm

  • sys/uvm/uvm_glue.c
  • /*	$OpenBSD: uvm_glue.c,v 1.95 2026/02/11 22:34:40 deraadt Exp $	*/
    /*	$NetBSD: uvm_glue.c,v 1.44 2001/02/06 19:54:44 eeh Exp $	*/
    
    /* 
     * Copyright (c) 1997 Charles D. Cranor and Washington University.
     * Copyright (c) 1991, 1993, The Regents of the University of California.  
     *
     * All rights reserved.
     *
     * This code is derived from software contributed to Berkeley by
     * The Mach Operating System project at Carnegie-Mellon University.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. Neither the name of the University nor the names of its contributors
     *    may be used to endorse or promote products derived from this software
     *    without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     *
     *	@(#)vm_glue.c	8.6 (Berkeley) 1/5/94
     * from: Id: uvm_glue.c,v 1.1.2.8 1998/02/07 01:16:54 chs Exp
     *
     *
     * Copyright (c) 1987, 1990 Carnegie-Mellon University.
     * All rights reserved.
     * 
     * Permission to use, copy, modify and distribute this software and
     * its documentation is hereby granted, provided that both the copyright
     * notice and this permission notice appear in all copies of the
     * software, derivative works or modified versions, and any portions
     * thereof, and that both notices appear in supporting documentation.
     * 
     * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 
     * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 
     * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     * 
     * Carnegie Mellon requests users of this software to return to
     *
     *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
     *  School of Computer Science
     *  Carnegie Mellon University
     *  Pittsburgh PA 15213-3890
     *
     * any improvements or extensions that they make and grant Carnegie the
     * rights to redistribute these changes.
     */
    
    /*
     * uvm_glue.c: glue functions
     */
    
    #include <sys/param.h>
    #include <sys/systm.h>
    #include <sys/proc.h>
    #include <sys/resourcevar.h>
    #include <sys/buf.h>
    #ifdef SYSVSHM
    #include <sys/shm.h>
    #endif
    
    #include <uvm/uvm.h>
    
    /*
     * uvm_kernacc: can the kernel access a region of memory
     *
     * - called from malloc [DIAGNOSTIC], and /dev/kmem driver (mem.c)
     */
    boolean_t
    uvm_kernacc(caddr_t addr, size_t len, int rw)
    {
    	boolean_t rv;
    	vaddr_t saddr, eaddr;
    	vm_prot_t prot = rw == B_READ ? PROT_READ : PROT_WRITE;
    
    	saddr = trunc_page((vaddr_t)addr);
    	eaddr = round_page((vaddr_t)addr + len);
    	vm_map_lock_read(kernel_map);
    	rv = uvm_map_checkprot(kernel_map, saddr, eaddr, prot);
    	vm_map_unlock_read(kernel_map);
    
    	return rv;
    }
    
    /*
     * uvm_vslock: wire user memory for I/O
     *
     * - called from sys_sysctl
     */
    int
    uvm_vslock(struct proc *p, caddr_t addr, size_t len, vm_prot_t access_type)
    {
    	struct vm_map *map = &p->p_vmspace->vm_map;
    	vaddr_t start, end;
    
    	start = trunc_page((vaddr_t)addr);
    	end = round_page((vaddr_t)addr + len);
    	if (end <= start)
    		return (EINVAL);
    
    	return uvm_map_pageable(map, start, end, FALSE, 0);
    }
    
    /*
     * uvm_vsunlock: unwire user memory wired by uvm_vslock()
     *
     * - called from sys_sysctl
     */
    void
    uvm_vsunlock(struct proc *p, caddr_t addr, size_t len)
    {
    	struct vm_map *map = &p->p_vmspace->vm_map;
    	vaddr_t start, end;
    
    	start = trunc_page((vaddr_t)addr);
    	end = round_page((vaddr_t)addr + len);
    	KASSERT(end > start);
    
    	uvm_map_pageable(map, start, end, TRUE, 0);
    }
    
    /*
     * uvm_vslock_device: wire user memory, make sure it's device reachable
     *  and bounce if necessary.
     *
     * - called from physio
     */
    int
    uvm_vslock_device(struct proc *p, void *addr, size_t len,
        vm_prot_t access_type, void **retp)
    {
    	struct vm_map *map = &p->p_vmspace->vm_map;
    	struct vm_page *pg;
    	struct pglist pgl;
    	int npages;
    	vaddr_t start, end, off;
    	vaddr_t sva, va;
    	vsize_t sz;
    	int error, mapv, i;
    
    	start = trunc_page((vaddr_t)addr);
    	end = round_page((vaddr_t)addr + len);
    	sz = end - start;
    	off = (vaddr_t)addr - start;
    	if (end <= start)
    		return (EINVAL);
    
    	vm_map_lock_read(map);
    retry:
    	mapv = map->timestamp;
    	vm_map_unlock_read(map);
    
    	if ((error = uvm_fault_wire(map, start, end, access_type)))
    		return (error);
    
    	vm_map_lock_read(map);
    	if (mapv != map->timestamp)
    		goto retry;
    
    	npages = atop(sz);
    	for (i = 0; i < npages; i++) {
    		paddr_t pa;
    
    		if (!pmap_extract(map->pmap, start + ptoa(i), &pa)) {
    			error = EFAULT;
    			goto out_unwire;
    		}
    		if (!PADDR_IS_DMA_REACHABLE(pa))
    			break;
    	}
    	if (i == npages) {
    		*retp = NULL;
    		return (0);
    	}
    
    	va = (vaddr_t)km_alloc(sz, &kv_any, &kp_none, &kd_nowait);
    	if (va == 0) {
    		error = ENOMEM;
    		goto out_unwire;
    	}
    	sva = va;
    
    	TAILQ_INIT(&pgl);
    	error = uvm_pglistalloc(npages * PAGE_SIZE, dma_constraint.ucr_low,
    	    dma_constraint.ucr_high, 0, 0, &pgl, npages, UVM_PLA_WAITOK);
    	if (error)
    		goto out_unmap;
    
    	while ((pg = TAILQ_FIRST(&pgl)) != NULL) {
    		TAILQ_REMOVE(&pgl, pg, pageq);
    		pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), PROT_READ | PROT_WRITE);
    		va += PAGE_SIZE;
    	}
    	pmap_update(pmap_kernel());
    	KASSERT(va == sva + sz);
    	*retp = (void *)(sva + off);
    
    	if ((error = copyin(addr, *retp, len)) == 0)
    		return 0;
    
    	uvm_km_pgremove_intrsafe(sva, sva + sz);
    	pmap_kremove(sva, sz);
    	pmap_update(pmap_kernel());
    out_unmap:
    	km_free((void *)sva, sz, &kv_any, &kp_none);
    out_unwire:
    	uvm_fault_unwire_locked(map, start, end);
    	vm_map_unlock_read(map);
    	return (error);
    }
    
    /*
     * uvm_vsunlock_device: unwire user memory wired by uvm_vslock_device()
     *
     * - called from physio
     */
    void
    uvm_vsunlock_device(struct proc *p, void *addr, size_t len, void *map)
    {
    	vaddr_t start, end;
    	vaddr_t kva;
    	vsize_t sz;
    
    	start = trunc_page((vaddr_t)addr);
    	end = round_page((vaddr_t)addr + len);
    	KASSERT(end > start);
    	sz = end - start;
    
    	if (map)
    		copyout(map, addr, len);
    
    	uvm_fault_unwire_locked(&p->p_vmspace->vm_map, start, end);
    	vm_map_unlock_read(&p->p_vmspace->vm_map);
    
    	if (!map)
    		return;
    
    	kva = trunc_page((vaddr_t)map);
    	uvm_km_pgremove_intrsafe(kva, kva + sz);
    	pmap_kremove(kva, sz);
    	pmap_update(pmap_kernel());
    	km_free((void *)kva, sz, &kv_any, &kp_none);
    }
    
    const struct kmem_va_mode kv_uarea = {
    	.kv_map = &kernel_map,
    	.kv_align = USPACE_ALIGN
    };
    
    /*
     * uvm_uarea_alloc: allocate the u-area for a new thread
     */
    vaddr_t
    uvm_uarea_alloc(void)
    {
    	vaddr_t va;
    
    	va = (vaddr_t)km_alloc(USPACE, &kv_uarea, &kp_zero, &kd_waitok);
    
    #ifdef __HAVE_USPACE_GUARD
    	/* Carve out a guard page between the PCB and the stack. */
    	if (va) {
    		struct vm_page *pg = NULL;
    		paddr_t pa;
    
    		if (pmap_extract(pmap_kernel(), va + PAGE_SIZE, &pa))
    			pg = PHYS_TO_VM_PAGE(pa);
    		pmap_kremove(va + PAGE_SIZE, PAGE_SIZE);
    		pmap_update(pmap_kernel());
    		if (pg)
    			uvm_pagefree(pg);
    	}
    #endif
    
    	return va;
    }
    
    /*
     * uvm_uarea_free: free a dead thread's stack
     *
     * - the thread passed to us is a dead thread; we
     *   are running on a different context now (the reaper).
     */
    void
    uvm_uarea_free(struct proc *p)
    {
    	km_free(p->p_addr, USPACE, &kv_uarea, &kp_zero);
    	p->p_addr = NULL;
    }
    
    /*
     * uvm_purge: teardown a virtual address space.
     *
     * If multi-threaded, must be called by the last thread of a process.
     */
    void
    uvm_purge(void)
    {
    	struct proc *p = curproc;
    	struct vmspace *vm = p->p_vmspace;
    
    	KERNEL_ASSERT_UNLOCKED();
    
    #ifdef __HAVE_PMAP_PURGE
    	pmap_purge(p);
    #endif
    	uvmspace_purge(vm);
    }
    
    /*
     * uvm_exit: exit a virtual address space
     */
    void
    uvm_exit(struct process *pr)
    {
    	struct vmspace *vm = pr->ps_vmspace;
    
    	pr->ps_vmspace = NULL;
    	uvmspace_free(vm);
    }
    
    /*
     * uvm_init_limit: init per-process VM limits
     *
     * - called for process 0 and then inherited by all others.
     */
    void
    uvm_init_limits(struct plimit *limit0)
    {
    	/*
    	 * Set up the initial limits on process VM.  Set the maximum
    	 * resident set size to be all of (reasonably) available memory.
    	 * This causes any single, large process to start random page
    	 * replacement once it fills memory.
    	 */
    	limit0->pl_rlimit[RLIMIT_STACK].rlim_cur = DFLSSIZ;
    	limit0->pl_rlimit[RLIMIT_STACK].rlim_max = MAXSSIZ;
    	limit0->pl_rlimit[RLIMIT_DATA].rlim_cur = DFLDSIZ;
    	limit0->pl_rlimit[RLIMIT_DATA].rlim_max = MAXDSIZ;
    	limit0->pl_rlimit[RLIMIT_RSS].rlim_cur = ptoa(atomic_load_sint(&uvmexp.free));
    }
    
    /*
     * uvm_atopg: convert KVAs back to their page structures.
     */
    struct vm_page *
    uvm_atopg(vaddr_t kva)
    {
    	struct vm_page *pg;
    	paddr_t pa;
    	boolean_t rv;
     
    	rv = pmap_extract(pmap_kernel(), kva, &pa);
    	KASSERT(rv);
    	pg = PHYS_TO_VM_PAGE(pa);
    	KASSERT(pg != NULL);
    	return (pg);
    }
    
    #ifndef SMALL_KERNEL
    int
    fill_vmmap(struct process *pr, struct kinfo_vmentry *kve,
        size_t *lenp)
    {
    	struct vm_map *map;
    
    	if (pr != NULL)
    		map = &pr->ps_vmspace->vm_map;
    	else
    		map = kernel_map;
    	return uvm_map_fill_vmmap(map, kve, lenp);
    }
    #endif