17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*07b65a64Saguzovsk * Common Development and Distribution License (the "License"). 6*07b65a64Saguzovsk * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*07b65a64Saguzovsk * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 277c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate #include <sys/types.h> 337c478bd9Sstevel@tonic-gate #include <sys/inttypes.h> 347c478bd9Sstevel@tonic-gate #include <sys/param.h> 357c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 367c478bd9Sstevel@tonic-gate #include <sys/systm.h> 377c478bd9Sstevel@tonic-gate #include <sys/signal.h> 387c478bd9Sstevel@tonic-gate #include <sys/user.h> 397c478bd9Sstevel@tonic-gate #include <sys/errno.h> 407c478bd9Sstevel@tonic-gate #include <sys/var.h> 417c478bd9Sstevel@tonic-gate #include <sys/proc.h> 427c478bd9Sstevel@tonic-gate #include <sys/tuneable.h> 437c478bd9Sstevel@tonic-gate #include <sys/debug.h> 447c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 457c478bd9Sstevel@tonic-gate #include <sys/cred.h> 467c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 477c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 487c478bd9Sstevel@tonic-gate #include <sys/vm.h> 497c478bd9Sstevel@tonic-gate #include <sys/file.h> 507c478bd9Sstevel@tonic-gate #include <sys/mman.h> 517c478bd9Sstevel@tonic-gate #include <sys/vmparam.h> 527c478bd9Sstevel@tonic-gate #include <sys/fcntl.h> 537c478bd9Sstevel@tonic-gate #include <sys/lwpchan_impl.h> 547c478bd9Sstevel@tonic-gate 557c478bd9Sstevel@tonic-gate #include <vm/hat.h> 567c478bd9Sstevel@tonic-gate #include <vm/as.h> 577c478bd9Sstevel@tonic-gate #include <vm/seg.h> 587c478bd9Sstevel@tonic-gate #include <vm/seg_dev.h> 597c478bd9Sstevel@tonic-gate #include <vm/seg_vn.h> 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate int use_brk_lpg = 1; 627c478bd9Sstevel@tonic-gate int use_stk_lpg = 1; 637c478bd9Sstevel@tonic-gate int use_zmap_lpg = 1; 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate static int brk_lpg(caddr_t nva); 667c478bd9Sstevel@tonic-gate static int grow_lpg(caddr_t sp); 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate int 697c478bd9Sstevel@tonic-gate brk(caddr_t nva) 707c478bd9Sstevel@tonic-gate { 717c478bd9Sstevel@tonic-gate int error; 727c478bd9Sstevel@tonic-gate proc_t *p = curproc; 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate /* 757c478bd9Sstevel@tonic-gate * Serialize brk operations on an address space. 767c478bd9Sstevel@tonic-gate * This also serves as the lock protecting p_brksize 777c478bd9Sstevel@tonic-gate * and p_brkpageszc. 787c478bd9Sstevel@tonic-gate */ 797c478bd9Sstevel@tonic-gate as_rangelock(p->p_as); 807c478bd9Sstevel@tonic-gate if (use_brk_lpg && (p->p_flag & SAUTOLPG) != 0) { 817c478bd9Sstevel@tonic-gate error = brk_lpg(nva); 827c478bd9Sstevel@tonic-gate } else { 837c478bd9Sstevel@tonic-gate error = brk_internal(nva, p->p_brkpageszc); 847c478bd9Sstevel@tonic-gate } 857c478bd9Sstevel@tonic-gate as_rangeunlock(p->p_as); 867c478bd9Sstevel@tonic-gate return ((error != 0 ? set_errno(error) : 0)); 877c478bd9Sstevel@tonic-gate } 887c478bd9Sstevel@tonic-gate 897c478bd9Sstevel@tonic-gate /* 907c478bd9Sstevel@tonic-gate * Algorithm: call arch-specific map_pgsz to get best page size to use, 917c478bd9Sstevel@tonic-gate * then call brk_internal(). 927c478bd9Sstevel@tonic-gate * Returns 0 on success. 937c478bd9Sstevel@tonic-gate */ 947c478bd9Sstevel@tonic-gate static int 957c478bd9Sstevel@tonic-gate brk_lpg(caddr_t nva) 967c478bd9Sstevel@tonic-gate { 977c478bd9Sstevel@tonic-gate struct proc *p = curproc; 987c478bd9Sstevel@tonic-gate size_t pgsz, len; 997c478bd9Sstevel@tonic-gate caddr_t addr; 1007c478bd9Sstevel@tonic-gate caddr_t bssbase = p->p_bssbase; 1017c478bd9Sstevel@tonic-gate caddr_t brkbase = p->p_brkbase; 1027c478bd9Sstevel@tonic-gate int oszc, szc; 1037c478bd9Sstevel@tonic-gate int err; 1047c478bd9Sstevel@tonic-gate int remap = 0; 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate oszc = p->p_brkpageszc; 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate /* 1097c478bd9Sstevel@tonic-gate * If p_brkbase has not yet been set, the first call 1107c478bd9Sstevel@tonic-gate * to brk_internal() will initialize it. 1117c478bd9Sstevel@tonic-gate */ 1127c478bd9Sstevel@tonic-gate if (brkbase == 0) { 1137c478bd9Sstevel@tonic-gate return (brk_internal(nva, oszc)); 1147c478bd9Sstevel@tonic-gate } 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate len = nva - bssbase; 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate pgsz = map_pgsz(MAPPGSZ_HEAP, p, bssbase, len, &remap); 1197c478bd9Sstevel@tonic-gate szc = page_szc(pgsz); 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate /* 1227c478bd9Sstevel@tonic-gate * Covers two cases: 1237c478bd9Sstevel@tonic-gate * 1. page_szc() returns -1 for invalid page size, so we want to 1247c478bd9Sstevel@tonic-gate * ignore it in that case. 1257c478bd9Sstevel@tonic-gate * 2. By design we never decrease page size, as it is more stable. 1267c478bd9Sstevel@tonic-gate */ 1277c478bd9Sstevel@tonic-gate if (szc <= oszc) { 1287c478bd9Sstevel@tonic-gate err = brk_internal(nva, oszc); 1297c478bd9Sstevel@tonic-gate /* If failed, back off to base page size. */ 1307c478bd9Sstevel@tonic-gate if (err != 0 && oszc != 0) { 1317c478bd9Sstevel@tonic-gate err = brk_internal(nva, 0); 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate return (err); 1347c478bd9Sstevel@tonic-gate } 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate if (remap == 0) { 1377c478bd9Sstevel@tonic-gate /* 1387c478bd9Sstevel@tonic-gate * Map from the current brk end up to the new page size 1397c478bd9Sstevel@tonic-gate * alignment using the current page size. 1407c478bd9Sstevel@tonic-gate */ 1417c478bd9Sstevel@tonic-gate addr = brkbase + p->p_brksize; 1427c478bd9Sstevel@tonic-gate addr = (caddr_t)P2ROUNDUP((uintptr_t)addr, pgsz); 1437c478bd9Sstevel@tonic-gate if (addr < nva) { 1447c478bd9Sstevel@tonic-gate err = brk_internal(addr, oszc); 1457c478bd9Sstevel@tonic-gate /* 1467c478bd9Sstevel@tonic-gate * In failure case, try again if oszc is not base page 1477c478bd9Sstevel@tonic-gate * size, then return err. 1487c478bd9Sstevel@tonic-gate */ 1497c478bd9Sstevel@tonic-gate if (err != 0) { 1507c478bd9Sstevel@tonic-gate if (oszc != 0) { 1517c478bd9Sstevel@tonic-gate err = brk_internal(nva, 0); 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate return (err); 1547c478bd9Sstevel@tonic-gate } 1557c478bd9Sstevel@tonic-gate } 1567c478bd9Sstevel@tonic-gate } 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate err = brk_internal(nva, szc); 1597c478bd9Sstevel@tonic-gate /* If using szc failed, map with base page size and return. */ 1607c478bd9Sstevel@tonic-gate if (err != 0) { 1617c478bd9Sstevel@tonic-gate if (szc != 0) { 1627c478bd9Sstevel@tonic-gate err = brk_internal(nva, 0); 1637c478bd9Sstevel@tonic-gate } 1647c478bd9Sstevel@tonic-gate return (err); 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate if (remap != 0) { 1687c478bd9Sstevel@tonic-gate /* 1697c478bd9Sstevel@tonic-gate * Round up brk base to a large page boundary and remap 1707c478bd9Sstevel@tonic-gate * anything in the segment already faulted in beyond that 1717c478bd9Sstevel@tonic-gate * point. 1727c478bd9Sstevel@tonic-gate */ 1737c478bd9Sstevel@tonic-gate addr = (caddr_t)P2ROUNDUP((uintptr_t)p->p_bssbase, pgsz); 1747c478bd9Sstevel@tonic-gate len = (brkbase + p->p_brksize) - addr; 1757c478bd9Sstevel@tonic-gate /* advisory, so ignore errors */ 1767c478bd9Sstevel@tonic-gate (void) as_setpagesize(p->p_as, addr, len, szc, B_FALSE); 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate ASSERT(err == 0); 1807c478bd9Sstevel@tonic-gate return (err); /* should always be 0 */ 1817c478bd9Sstevel@tonic-gate } 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate /* 1847c478bd9Sstevel@tonic-gate * Returns 0 on success. 1857c478bd9Sstevel@tonic-gate */ 1867c478bd9Sstevel@tonic-gate int 1877c478bd9Sstevel@tonic-gate brk_internal(caddr_t nva, uint_t brkszc) 1887c478bd9Sstevel@tonic-gate { 1897c478bd9Sstevel@tonic-gate caddr_t ova; /* current break address */ 1907c478bd9Sstevel@tonic-gate size_t size; 1917c478bd9Sstevel@tonic-gate int error; 1927c478bd9Sstevel@tonic-gate struct proc *p = curproc; 1937c478bd9Sstevel@tonic-gate struct as *as = p->p_as; 1947c478bd9Sstevel@tonic-gate size_t pgsz; 1957c478bd9Sstevel@tonic-gate uint_t szc; 1967c478bd9Sstevel@tonic-gate rctl_qty_t as_rctl; 1977c478bd9Sstevel@tonic-gate 1987c478bd9Sstevel@tonic-gate /* 1997c478bd9Sstevel@tonic-gate * extend heap to brkszc alignment but use current p->p_brkpageszc 2007c478bd9Sstevel@tonic-gate * for the newly created segment. This allows the new extension 2017c478bd9Sstevel@tonic-gate * segment to be concatenated successfully with the existing brk 2027c478bd9Sstevel@tonic-gate * segment. 2037c478bd9Sstevel@tonic-gate */ 2047c478bd9Sstevel@tonic-gate if ((szc = brkszc) != 0) { 2057c478bd9Sstevel@tonic-gate pgsz = page_get_pagesize(szc); 2067c478bd9Sstevel@tonic-gate ASSERT(pgsz > PAGESIZE); 2077c478bd9Sstevel@tonic-gate } else { 2087c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 2097c478bd9Sstevel@tonic-gate } 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 2127c478bd9Sstevel@tonic-gate as_rctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_DATA], 2137c478bd9Sstevel@tonic-gate p->p_rctls, p); 2147c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate /* 2177c478bd9Sstevel@tonic-gate * If p_brkbase has not yet been set, the first call 2187c478bd9Sstevel@tonic-gate * to brk() will initialize it. 2197c478bd9Sstevel@tonic-gate */ 2207c478bd9Sstevel@tonic-gate if (p->p_brkbase == 0) 2217c478bd9Sstevel@tonic-gate p->p_brkbase = nva; 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate /* 2247c478bd9Sstevel@tonic-gate * Before multiple page size support existed p_brksize was the value 2257c478bd9Sstevel@tonic-gate * not rounded to the pagesize (i.e. it stored the exact user request 2267c478bd9Sstevel@tonic-gate * for heap size). If pgsz is greater than PAGESIZE calculate the 2277c478bd9Sstevel@tonic-gate * heap size as the real new heap size by rounding it up to pgsz. 2287c478bd9Sstevel@tonic-gate * This is useful since we may want to know where the heap ends 2297c478bd9Sstevel@tonic-gate * without knowing heap pagesize (e.g. some old code) and also if 2307c478bd9Sstevel@tonic-gate * heap pagesize changes we can update p_brkpageszc but delay adding 2317c478bd9Sstevel@tonic-gate * new mapping yet still know from p_brksize where the heap really 2327c478bd9Sstevel@tonic-gate * ends. The user requested heap end is stored in libc variable. 2337c478bd9Sstevel@tonic-gate */ 2347c478bd9Sstevel@tonic-gate if (pgsz > PAGESIZE) { 2357c478bd9Sstevel@tonic-gate caddr_t tnva = (caddr_t)P2ROUNDUP((uintptr_t)nva, pgsz); 2367c478bd9Sstevel@tonic-gate size = tnva - p->p_brkbase; 2377c478bd9Sstevel@tonic-gate if (tnva < p->p_brkbase || (size > p->p_brksize && 2387c478bd9Sstevel@tonic-gate size > (size_t)as_rctl)) { 2397c478bd9Sstevel@tonic-gate szc = 0; 2407c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 2417c478bd9Sstevel@tonic-gate size = nva - p->p_brkbase; 2427c478bd9Sstevel@tonic-gate } 2437c478bd9Sstevel@tonic-gate } else { 2447c478bd9Sstevel@tonic-gate size = nva - p->p_brkbase; 2457c478bd9Sstevel@tonic-gate } 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gate /* 2487c478bd9Sstevel@tonic-gate * use PAGESIZE to roundup ova because we want to know the real value 2497c478bd9Sstevel@tonic-gate * of the current heap end in case p_brkpageszc changes since the last 2507c478bd9Sstevel@tonic-gate * p_brksize was computed. 2517c478bd9Sstevel@tonic-gate */ 2527c478bd9Sstevel@tonic-gate nva = (caddr_t)P2ROUNDUP((uintptr_t)nva, pgsz); 2537c478bd9Sstevel@tonic-gate ova = (caddr_t)P2ROUNDUP((uintptr_t)(p->p_brkbase + p->p_brksize), 2547c478bd9Sstevel@tonic-gate PAGESIZE); 2557c478bd9Sstevel@tonic-gate 2567c478bd9Sstevel@tonic-gate if ((nva < p->p_brkbase) || (size > p->p_brksize && 2577c478bd9Sstevel@tonic-gate size > as_rctl)) { 2587c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 2597c478bd9Sstevel@tonic-gate (void) rctl_action(rctlproc_legacy[RLIMIT_DATA], p->p_rctls, p, 2607c478bd9Sstevel@tonic-gate RCA_SAFE); 2617c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 2627c478bd9Sstevel@tonic-gate return (ENOMEM); 2637c478bd9Sstevel@tonic-gate } 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate if (nva > ova) { 2667c478bd9Sstevel@tonic-gate struct segvn_crargs crargs = 2677c478bd9Sstevel@tonic-gate SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate if (!(p->p_datprot & PROT_EXEC)) { 2707c478bd9Sstevel@tonic-gate crargs.prot &= ~PROT_EXEC; 2717c478bd9Sstevel@tonic-gate } 2727c478bd9Sstevel@tonic-gate 2737c478bd9Sstevel@tonic-gate /* 2747c478bd9Sstevel@tonic-gate * Add new zfod mapping to extend UNIX data segment 2757c478bd9Sstevel@tonic-gate */ 2767c478bd9Sstevel@tonic-gate crargs.szc = szc; 2777c478bd9Sstevel@tonic-gate crargs.lgrp_mem_policy_flags = LGRP_MP_FLAG_EXTEND_UP; 2787c478bd9Sstevel@tonic-gate error = as_map(as, ova, (size_t)(nva - ova), segvn_create, 2797c478bd9Sstevel@tonic-gate &crargs); 2807c478bd9Sstevel@tonic-gate if (error) { 2817c478bd9Sstevel@tonic-gate return (error); 2827c478bd9Sstevel@tonic-gate } 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate } else if (nva < ova) { 2857c478bd9Sstevel@tonic-gate /* 2867c478bd9Sstevel@tonic-gate * Release mapping to shrink UNIX data segment. 2877c478bd9Sstevel@tonic-gate */ 2887c478bd9Sstevel@tonic-gate (void) as_unmap(as, nva, (size_t)(ova - nva)); 2897c478bd9Sstevel@tonic-gate } 2907c478bd9Sstevel@tonic-gate p->p_brksize = size; 2917c478bd9Sstevel@tonic-gate p->p_brkpageszc = szc; 2927c478bd9Sstevel@tonic-gate return (0); 2937c478bd9Sstevel@tonic-gate } 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate /* 2967c478bd9Sstevel@tonic-gate * Grow the stack to include sp. Return 1 if successful, 0 otherwise. 2977c478bd9Sstevel@tonic-gate * This routine assumes that the stack grows downward. 2987c478bd9Sstevel@tonic-gate */ 2997c478bd9Sstevel@tonic-gate int 3007c478bd9Sstevel@tonic-gate grow(caddr_t sp) 3017c478bd9Sstevel@tonic-gate { 3027c478bd9Sstevel@tonic-gate struct proc *p = curproc; 3037c478bd9Sstevel@tonic-gate int err; 3047c478bd9Sstevel@tonic-gate 3057c478bd9Sstevel@tonic-gate /* 3067c478bd9Sstevel@tonic-gate * Serialize grow operations on an address space. 3077c478bd9Sstevel@tonic-gate * This also serves as the lock protecting p_stksize 3087c478bd9Sstevel@tonic-gate * and p_stkpageszc. 3097c478bd9Sstevel@tonic-gate */ 3107c478bd9Sstevel@tonic-gate as_rangelock(p->p_as); 3117c478bd9Sstevel@tonic-gate if (use_stk_lpg && (p->p_flag & SAUTOLPG) != 0) { 3127c478bd9Sstevel@tonic-gate err = grow_lpg(sp); 3137c478bd9Sstevel@tonic-gate } else { 3147c478bd9Sstevel@tonic-gate err = grow_internal(sp, p->p_stkpageszc); 3157c478bd9Sstevel@tonic-gate } 3167c478bd9Sstevel@tonic-gate as_rangeunlock(p->p_as); 3177c478bd9Sstevel@tonic-gate return ((err == 0 ? 1 : 0)); 3187c478bd9Sstevel@tonic-gate } 3197c478bd9Sstevel@tonic-gate 3207c478bd9Sstevel@tonic-gate /* 3217c478bd9Sstevel@tonic-gate * Algorithm: call arch-specific map_pgsz to get best page size to use, 3227c478bd9Sstevel@tonic-gate * then call grow_internal(). 3237c478bd9Sstevel@tonic-gate * Returns 0 on success. 3247c478bd9Sstevel@tonic-gate */ 3257c478bd9Sstevel@tonic-gate static int 3267c478bd9Sstevel@tonic-gate grow_lpg(caddr_t sp) 3277c478bd9Sstevel@tonic-gate { 3287c478bd9Sstevel@tonic-gate struct proc *p = curproc; 3297c478bd9Sstevel@tonic-gate size_t pgsz; 3307c478bd9Sstevel@tonic-gate size_t len, newsize; 3317c478bd9Sstevel@tonic-gate caddr_t addr, oldsp; 3327c478bd9Sstevel@tonic-gate int oszc, szc; 3337c478bd9Sstevel@tonic-gate int err; 3347c478bd9Sstevel@tonic-gate int remap = 0; 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate newsize = p->p_usrstack - sp; 3377c478bd9Sstevel@tonic-gate 3387c478bd9Sstevel@tonic-gate oszc = p->p_stkpageszc; 3397c478bd9Sstevel@tonic-gate pgsz = map_pgsz(MAPPGSZ_STK, p, sp, newsize, &remap); 3407c478bd9Sstevel@tonic-gate szc = page_szc(pgsz); 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate /* 3437c478bd9Sstevel@tonic-gate * Covers two cases: 3447c478bd9Sstevel@tonic-gate * 1. page_szc() returns -1 for invalid page size, so we want to 3457c478bd9Sstevel@tonic-gate * ignore it in that case. 3467c478bd9Sstevel@tonic-gate * 2. By design we never decrease page size, as it is more stable. 3477c478bd9Sstevel@tonic-gate * This shouldn't happen as the stack never shrinks. 3487c478bd9Sstevel@tonic-gate */ 3497c478bd9Sstevel@tonic-gate if (szc <= oszc) { 3507c478bd9Sstevel@tonic-gate err = grow_internal(sp, oszc); 3517c478bd9Sstevel@tonic-gate /* failed, fall back to base page size */ 3527c478bd9Sstevel@tonic-gate if (err != 0 && oszc != 0) { 3537c478bd9Sstevel@tonic-gate err = grow_internal(sp, 0); 3547c478bd9Sstevel@tonic-gate } 3557c478bd9Sstevel@tonic-gate return (err); 3567c478bd9Sstevel@tonic-gate } 3577c478bd9Sstevel@tonic-gate 3587c478bd9Sstevel@tonic-gate /* 3597c478bd9Sstevel@tonic-gate * We've grown sufficiently to switch to a new page size. 3607c478bd9Sstevel@tonic-gate * If we're not going to remap the whole segment with the new 3617c478bd9Sstevel@tonic-gate * page size, split the grow into two operations: map to the new 3627c478bd9Sstevel@tonic-gate * page size alignment boundary with the existing page size, then 3637c478bd9Sstevel@tonic-gate * map the rest with the new page size. 3647c478bd9Sstevel@tonic-gate */ 3657c478bd9Sstevel@tonic-gate err = 0; 3667c478bd9Sstevel@tonic-gate if (remap == 0) { 3677c478bd9Sstevel@tonic-gate oldsp = p->p_usrstack - p->p_stksize; 3687c478bd9Sstevel@tonic-gate addr = (caddr_t)P2ALIGN((uintptr_t)oldsp, pgsz); 3697c478bd9Sstevel@tonic-gate if (addr > sp) { 3707c478bd9Sstevel@tonic-gate err = grow_internal(addr, oszc); 3717c478bd9Sstevel@tonic-gate /* 3727c478bd9Sstevel@tonic-gate * In this case, grow with oszc failed, so grow all the 3737c478bd9Sstevel@tonic-gate * way to sp with base page size. 3747c478bd9Sstevel@tonic-gate */ 3757c478bd9Sstevel@tonic-gate if (err != 0) { 3767c478bd9Sstevel@tonic-gate if (oszc != 0) { 3777c478bd9Sstevel@tonic-gate err = grow_internal(sp, 0); 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate return (err); 3807c478bd9Sstevel@tonic-gate } 3817c478bd9Sstevel@tonic-gate } 3827c478bd9Sstevel@tonic-gate } 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate err = grow_internal(sp, szc); 3857c478bd9Sstevel@tonic-gate /* The grow with szc failed, so fall back to base page size. */ 3867c478bd9Sstevel@tonic-gate if (err != 0) { 3877c478bd9Sstevel@tonic-gate if (szc != 0) { 3887c478bd9Sstevel@tonic-gate err = grow_internal(sp, 0); 3897c478bd9Sstevel@tonic-gate } 3907c478bd9Sstevel@tonic-gate return (err); 3917c478bd9Sstevel@tonic-gate } 3927c478bd9Sstevel@tonic-gate 3937c478bd9Sstevel@tonic-gate if (remap) { 3947c478bd9Sstevel@tonic-gate /* 3957c478bd9Sstevel@tonic-gate * Round up stack pointer to a large page boundary and remap 3967c478bd9Sstevel@tonic-gate * any pgsz pages in the segment already faulted in beyond that 3977c478bd9Sstevel@tonic-gate * point. 3987c478bd9Sstevel@tonic-gate */ 3997c478bd9Sstevel@tonic-gate addr = p->p_usrstack - p->p_stksize; 4007c478bd9Sstevel@tonic-gate addr = (caddr_t)P2ROUNDUP((uintptr_t)addr, pgsz); 4017c478bd9Sstevel@tonic-gate len = (caddr_t)P2ALIGN((uintptr_t)p->p_usrstack, pgsz) - addr; 4027c478bd9Sstevel@tonic-gate /* advisory, so ignore errors */ 4037c478bd9Sstevel@tonic-gate (void) as_setpagesize(p->p_as, addr, len, szc, B_FALSE); 4047c478bd9Sstevel@tonic-gate } 4057c478bd9Sstevel@tonic-gate 4067c478bd9Sstevel@tonic-gate /* Update page size code for stack. */ 4077c478bd9Sstevel@tonic-gate p->p_stkpageszc = szc; 4087c478bd9Sstevel@tonic-gate 4097c478bd9Sstevel@tonic-gate ASSERT(err == 0); 4107c478bd9Sstevel@tonic-gate return (err); /* should always be 0 */ 4117c478bd9Sstevel@tonic-gate } 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate /* 4147c478bd9Sstevel@tonic-gate * This routine assumes that the stack grows downward. 4157c478bd9Sstevel@tonic-gate * Returns 0 on success, errno on failure. 4167c478bd9Sstevel@tonic-gate */ 4177c478bd9Sstevel@tonic-gate int 4187c478bd9Sstevel@tonic-gate grow_internal(caddr_t sp, uint_t growszc) 4197c478bd9Sstevel@tonic-gate { 4207c478bd9Sstevel@tonic-gate struct proc *p = curproc; 4217c478bd9Sstevel@tonic-gate struct as *as = p->p_as; 4227c478bd9Sstevel@tonic-gate size_t newsize = p->p_usrstack - sp; 4237c478bd9Sstevel@tonic-gate size_t oldsize; 4247c478bd9Sstevel@tonic-gate int error; 4257c478bd9Sstevel@tonic-gate size_t pgsz; 4267c478bd9Sstevel@tonic-gate uint_t szc; 4277c478bd9Sstevel@tonic-gate struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate ASSERT(sp < p->p_usrstack); 4307c478bd9Sstevel@tonic-gate 4317c478bd9Sstevel@tonic-gate /* 4327c478bd9Sstevel@tonic-gate * grow to growszc alignment but use current p->p_stkpageszc for 4337c478bd9Sstevel@tonic-gate * the segvn_crargs szc passed to segvn_create. For memcntl to 4347c478bd9Sstevel@tonic-gate * increase the szc, this allows the new extension segment to be 4357c478bd9Sstevel@tonic-gate * concatenated successfully with the existing stack segment. 4367c478bd9Sstevel@tonic-gate */ 4377c478bd9Sstevel@tonic-gate if ((szc = growszc) != 0) { 4387c478bd9Sstevel@tonic-gate pgsz = page_get_pagesize(szc); 4397c478bd9Sstevel@tonic-gate ASSERT(pgsz > PAGESIZE); 4407c478bd9Sstevel@tonic-gate newsize = P2ROUNDUP(newsize, pgsz); 4417c478bd9Sstevel@tonic-gate if (newsize > (size_t)p->p_stk_ctl) { 4427c478bd9Sstevel@tonic-gate szc = 0; 4437c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 4447c478bd9Sstevel@tonic-gate newsize = p->p_usrstack - sp; 4457c478bd9Sstevel@tonic-gate } 4467c478bd9Sstevel@tonic-gate } else { 4477c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 4487c478bd9Sstevel@tonic-gate } 4497c478bd9Sstevel@tonic-gate 4507c478bd9Sstevel@tonic-gate if (newsize > (size_t)p->p_stk_ctl) { 4517c478bd9Sstevel@tonic-gate (void) rctl_action(rctlproc_legacy[RLIMIT_STACK], p->p_rctls, p, 4527c478bd9Sstevel@tonic-gate RCA_UNSAFE_ALL); 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate return (ENOMEM); 4557c478bd9Sstevel@tonic-gate } 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate oldsize = p->p_stksize; 4587c478bd9Sstevel@tonic-gate newsize = P2ROUNDUP(newsize, pgsz); 4597c478bd9Sstevel@tonic-gate ASSERT(P2PHASE(oldsize, PAGESIZE) == 0); 4607c478bd9Sstevel@tonic-gate 4617c478bd9Sstevel@tonic-gate if (newsize <= oldsize) { /* prevent the stack from shrinking */ 4627c478bd9Sstevel@tonic-gate return (0); 4637c478bd9Sstevel@tonic-gate } 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate if (!(p->p_stkprot & PROT_EXEC)) { 4667c478bd9Sstevel@tonic-gate crargs.prot &= ~PROT_EXEC; 4677c478bd9Sstevel@tonic-gate } 4687c478bd9Sstevel@tonic-gate /* 4697c478bd9Sstevel@tonic-gate * extend stack with the p_stkpageszc. growszc is different than 4707c478bd9Sstevel@tonic-gate * p_stkpageszc only on a memcntl to increase the stack pagesize. 4717c478bd9Sstevel@tonic-gate */ 4727c478bd9Sstevel@tonic-gate crargs.szc = p->p_stkpageszc; 4737c478bd9Sstevel@tonic-gate crargs.lgrp_mem_policy_flags = LGRP_MP_FLAG_EXTEND_DOWN; 4747c478bd9Sstevel@tonic-gate 4757c478bd9Sstevel@tonic-gate if ((error = as_map(as, p->p_usrstack - newsize, newsize - oldsize, 4767c478bd9Sstevel@tonic-gate segvn_create, &crargs)) != 0) { 4777c478bd9Sstevel@tonic-gate if (error == EAGAIN) { 4787c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "Sorry, no swap space to grow stack " 4797c478bd9Sstevel@tonic-gate "for pid %d (%s)", p->p_pid, u.u_comm); 4807c478bd9Sstevel@tonic-gate } 4817c478bd9Sstevel@tonic-gate return (error); 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate p->p_stksize = newsize; 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate 4867c478bd9Sstevel@tonic-gate /* 4877c478bd9Sstevel@tonic-gate * Set up translations so the process doesn't have to fault in 4887c478bd9Sstevel@tonic-gate * the stack pages we just gave it. 4897c478bd9Sstevel@tonic-gate */ 4907c478bd9Sstevel@tonic-gate (void) as_fault(as->a_hat, as, 4917c478bd9Sstevel@tonic-gate p->p_usrstack - newsize, newsize - oldsize, F_INVAL, S_WRITE); 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate return (0); 4947c478bd9Sstevel@tonic-gate } 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate /* 4977c478bd9Sstevel@tonic-gate * Used for MAP_ANON - fast way to get anonymous pages 4987c478bd9Sstevel@tonic-gate */ 4997c478bd9Sstevel@tonic-gate static int 5007c478bd9Sstevel@tonic-gate zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags, 5017c478bd9Sstevel@tonic-gate offset_t pos) 5027c478bd9Sstevel@tonic-gate { 5037c478bd9Sstevel@tonic-gate struct segvn_crargs a, b; 5047c478bd9Sstevel@tonic-gate struct proc *p = curproc; 5057c478bd9Sstevel@tonic-gate int err; 5067c478bd9Sstevel@tonic-gate size_t pgsz; 5077c478bd9Sstevel@tonic-gate size_t l0, l1, l2, l3, l4; /* 0th through 5th chunks */ 5087c478bd9Sstevel@tonic-gate caddr_t ruaddr, ruaddr0; /* rounded up addresses */ 5097c478bd9Sstevel@tonic-gate extern size_t auto_lpg_va_default; 5107c478bd9Sstevel@tonic-gate 5117c478bd9Sstevel@tonic-gate if (((PROT_ALL & uprot) != uprot)) 5127c478bd9Sstevel@tonic-gate return (EACCES); 5137c478bd9Sstevel@tonic-gate 5147c478bd9Sstevel@tonic-gate if ((flags & MAP_FIXED) != 0) { 5157c478bd9Sstevel@tonic-gate caddr_t userlimit; 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate /* 5187c478bd9Sstevel@tonic-gate * Use the user address. First verify that 5197c478bd9Sstevel@tonic-gate * the address to be used is page aligned. 5207c478bd9Sstevel@tonic-gate * Then make some simple bounds checks. 5217c478bd9Sstevel@tonic-gate */ 5227c478bd9Sstevel@tonic-gate if (((uintptr_t)*addrp & PAGEOFFSET) != 0) 5237c478bd9Sstevel@tonic-gate return (EINVAL); 5247c478bd9Sstevel@tonic-gate 5257c478bd9Sstevel@tonic-gate userlimit = flags & _MAP_LOW32 ? 5267c478bd9Sstevel@tonic-gate (caddr_t)USERLIMIT32 : as->a_userlimit; 5277c478bd9Sstevel@tonic-gate switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) { 5287c478bd9Sstevel@tonic-gate case RANGE_OKAY: 5297c478bd9Sstevel@tonic-gate break; 5307c478bd9Sstevel@tonic-gate case RANGE_BADPROT: 5317c478bd9Sstevel@tonic-gate return (ENOTSUP); 5327c478bd9Sstevel@tonic-gate case RANGE_BADADDR: 5337c478bd9Sstevel@tonic-gate default: 5347c478bd9Sstevel@tonic-gate return (ENOMEM); 5357c478bd9Sstevel@tonic-gate } 5367c478bd9Sstevel@tonic-gate (void) as_unmap(as, *addrp, len); 5377c478bd9Sstevel@tonic-gate } else { 5387c478bd9Sstevel@tonic-gate /* 5397c478bd9Sstevel@tonic-gate * No need to worry about vac alignment for anonymous 5407c478bd9Sstevel@tonic-gate * pages since this is a "clone" object that doesn't 5417c478bd9Sstevel@tonic-gate * yet exist. 5427c478bd9Sstevel@tonic-gate */ 5437c478bd9Sstevel@tonic-gate map_addr(addrp, len, pos, 0, flags); 5447c478bd9Sstevel@tonic-gate if (*addrp == NULL) 5457c478bd9Sstevel@tonic-gate return (ENOMEM); 5467c478bd9Sstevel@tonic-gate } 5477c478bd9Sstevel@tonic-gate 5487c478bd9Sstevel@tonic-gate /* 5497c478bd9Sstevel@tonic-gate * Use the seg_vn segment driver; passing in the NULL amp 5507c478bd9Sstevel@tonic-gate * gives the desired "cloning" effect. 5517c478bd9Sstevel@tonic-gate */ 5527c478bd9Sstevel@tonic-gate a.vp = NULL; 5537c478bd9Sstevel@tonic-gate a.offset = 0; 5547c478bd9Sstevel@tonic-gate a.type = flags & MAP_TYPE; 5557c478bd9Sstevel@tonic-gate a.prot = uprot; 5567c478bd9Sstevel@tonic-gate a.maxprot = PROT_ALL; 5577c478bd9Sstevel@tonic-gate a.flags = flags & ~MAP_TYPE; 5587c478bd9Sstevel@tonic-gate a.cred = CRED(); 5597c478bd9Sstevel@tonic-gate a.amp = NULL; 5607c478bd9Sstevel@tonic-gate a.szc = 0; 5617c478bd9Sstevel@tonic-gate a.lgrp_mem_policy_flags = 0; 5627c478bd9Sstevel@tonic-gate 5637c478bd9Sstevel@tonic-gate /* 5647c478bd9Sstevel@tonic-gate * Call arch-specific map_pgsz routine to pick best page size to map 5657c478bd9Sstevel@tonic-gate * this segment, and break the mapping up into parts if required. 5667c478bd9Sstevel@tonic-gate * 5677c478bd9Sstevel@tonic-gate * The parts work like this: 5687c478bd9Sstevel@tonic-gate * 5697c478bd9Sstevel@tonic-gate * addr --------- 5707c478bd9Sstevel@tonic-gate * | | l0 5717c478bd9Sstevel@tonic-gate * --------- 5727c478bd9Sstevel@tonic-gate * | | l1 5737c478bd9Sstevel@tonic-gate * --------- 5747c478bd9Sstevel@tonic-gate * | | l2 5757c478bd9Sstevel@tonic-gate * --------- 5767c478bd9Sstevel@tonic-gate * | | l3 5777c478bd9Sstevel@tonic-gate * --------- 5787c478bd9Sstevel@tonic-gate * | | l4 5797c478bd9Sstevel@tonic-gate * --------- 5807c478bd9Sstevel@tonic-gate * addr+len 5817c478bd9Sstevel@tonic-gate * 5827c478bd9Sstevel@tonic-gate * Starting from the middle, l2 is the number of bytes mapped by the 5837c478bd9Sstevel@tonic-gate * selected large page. l1 and l3 are mapped by auto_lpg_va_default 5847c478bd9Sstevel@tonic-gate * page size pages, and l0 and l4 are mapped by base page size pages. 5857c478bd9Sstevel@tonic-gate * If auto_lpg_va_default is the base page size, then l0 == l4 == 0. 5867c478bd9Sstevel@tonic-gate * If the requested address or length are aligned to the selected large 5877c478bd9Sstevel@tonic-gate * page size, l1 or l3 may also be 0. 5887c478bd9Sstevel@tonic-gate */ 589*07b65a64Saguzovsk if (use_zmap_lpg && a.type == MAP_PRIVATE) { 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate pgsz = map_pgsz(MAPPGSZ_VA, p, *addrp, len, NULL); 5927c478bd9Sstevel@tonic-gate if (pgsz <= PAGESIZE || len < pgsz) { 5937c478bd9Sstevel@tonic-gate return (as_map(as, *addrp, len, segvn_create, &a)); 5947c478bd9Sstevel@tonic-gate } 5957c478bd9Sstevel@tonic-gate 5967c478bd9Sstevel@tonic-gate ruaddr = (caddr_t)P2ROUNDUP((uintptr_t)*addrp, pgsz); 5977c478bd9Sstevel@tonic-gate if (auto_lpg_va_default != MMU_PAGESIZE) { 5987c478bd9Sstevel@tonic-gate ruaddr0 = (caddr_t)P2ROUNDUP((uintptr_t)*addrp, 5997c478bd9Sstevel@tonic-gate auto_lpg_va_default); 6007c478bd9Sstevel@tonic-gate l0 = ruaddr0 - *addrp; 6017c478bd9Sstevel@tonic-gate } else { 6027c478bd9Sstevel@tonic-gate l0 = 0; 6037c478bd9Sstevel@tonic-gate ruaddr0 = *addrp; 6047c478bd9Sstevel@tonic-gate } 6057c478bd9Sstevel@tonic-gate l1 = ruaddr - ruaddr0; 6067c478bd9Sstevel@tonic-gate l3 = P2PHASE(len - l0 - l1, pgsz); 6077c478bd9Sstevel@tonic-gate if (auto_lpg_va_default == MMU_PAGESIZE) { 6087c478bd9Sstevel@tonic-gate l4 = 0; 6097c478bd9Sstevel@tonic-gate } else { 6107c478bd9Sstevel@tonic-gate l4 = P2PHASE(l3, auto_lpg_va_default); 6117c478bd9Sstevel@tonic-gate l3 -= l4; 6127c478bd9Sstevel@tonic-gate } 6137c478bd9Sstevel@tonic-gate l2 = len - l0 - l1 - l3 - l4; 6147c478bd9Sstevel@tonic-gate 6157c478bd9Sstevel@tonic-gate if (l0) { 6167c478bd9Sstevel@tonic-gate b = a; 6177c478bd9Sstevel@tonic-gate err = as_map(as, *addrp, l0, segvn_create, &b); 6187c478bd9Sstevel@tonic-gate if (err) { 6197c478bd9Sstevel@tonic-gate return (err); 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate 6237c478bd9Sstevel@tonic-gate if (l1) { 6247c478bd9Sstevel@tonic-gate b = a; 6257c478bd9Sstevel@tonic-gate b.szc = page_szc(auto_lpg_va_default); 6267c478bd9Sstevel@tonic-gate err = as_map(as, ruaddr0, l1, segvn_create, &b); 6277c478bd9Sstevel@tonic-gate if (err) { 6287c478bd9Sstevel@tonic-gate goto error1; 6297c478bd9Sstevel@tonic-gate } 6307c478bd9Sstevel@tonic-gate } 6317c478bd9Sstevel@tonic-gate 6327c478bd9Sstevel@tonic-gate if (l2) { 6337c478bd9Sstevel@tonic-gate b = a; 6347c478bd9Sstevel@tonic-gate b.szc = page_szc(pgsz); 6357c478bd9Sstevel@tonic-gate err = as_map(as, ruaddr, l2, segvn_create, &b); 6367c478bd9Sstevel@tonic-gate if (err) { 6377c478bd9Sstevel@tonic-gate goto error2; 6387c478bd9Sstevel@tonic-gate } 6397c478bd9Sstevel@tonic-gate } 6407c478bd9Sstevel@tonic-gate 6417c478bd9Sstevel@tonic-gate if (l3) { 6427c478bd9Sstevel@tonic-gate b = a; 6437c478bd9Sstevel@tonic-gate b.szc = page_szc(auto_lpg_va_default); 6447c478bd9Sstevel@tonic-gate err = as_map(as, ruaddr + l2, l3, segvn_create, &b); 6457c478bd9Sstevel@tonic-gate if (err) { 6467c478bd9Sstevel@tonic-gate goto error3; 6477c478bd9Sstevel@tonic-gate } 6487c478bd9Sstevel@tonic-gate } 6497c478bd9Sstevel@tonic-gate if (l4) { 6507c478bd9Sstevel@tonic-gate err = as_map(as, ruaddr + l2 + l3, l4, segvn_create, 6517c478bd9Sstevel@tonic-gate &a); 6527c478bd9Sstevel@tonic-gate if (err) { 6537c478bd9Sstevel@tonic-gate error3: 6547c478bd9Sstevel@tonic-gate if (l3) { 6557c478bd9Sstevel@tonic-gate (void) as_unmap(as, ruaddr + l2, l3); 6567c478bd9Sstevel@tonic-gate } 6577c478bd9Sstevel@tonic-gate error2: 6587c478bd9Sstevel@tonic-gate if (l2) { 6597c478bd9Sstevel@tonic-gate (void) as_unmap(as, ruaddr, l2); 6607c478bd9Sstevel@tonic-gate } 6617c478bd9Sstevel@tonic-gate error1: 6627c478bd9Sstevel@tonic-gate if (l1) { 6637c478bd9Sstevel@tonic-gate (void) as_unmap(as, ruaddr0, l1); 6647c478bd9Sstevel@tonic-gate } 6657c478bd9Sstevel@tonic-gate if (l0) { 6667c478bd9Sstevel@tonic-gate (void) as_unmap(as, *addrp, l0); 6677c478bd9Sstevel@tonic-gate } 6687c478bd9Sstevel@tonic-gate return (err); 6697c478bd9Sstevel@tonic-gate } 6707c478bd9Sstevel@tonic-gate } 6717c478bd9Sstevel@tonic-gate 6727c478bd9Sstevel@tonic-gate return (0); 6737c478bd9Sstevel@tonic-gate } 6747c478bd9Sstevel@tonic-gate 6757c478bd9Sstevel@tonic-gate return (as_map(as, *addrp, len, segvn_create, &a)); 6767c478bd9Sstevel@tonic-gate } 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate static int 6797c478bd9Sstevel@tonic-gate smmap_common(caddr_t *addrp, size_t len, 6807c478bd9Sstevel@tonic-gate int prot, int flags, struct file *fp, offset_t pos) 6817c478bd9Sstevel@tonic-gate { 6827c478bd9Sstevel@tonic-gate struct vnode *vp; 6837c478bd9Sstevel@tonic-gate struct as *as = curproc->p_as; 6847c478bd9Sstevel@tonic-gate uint_t uprot, maxprot, type; 6857c478bd9Sstevel@tonic-gate int error; 6867c478bd9Sstevel@tonic-gate 6877c478bd9Sstevel@tonic-gate if ((flags & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | _MAP_NEW | 6887c478bd9Sstevel@tonic-gate _MAP_LOW32 | MAP_NORESERVE | MAP_ANON | MAP_ALIGN | 6897c478bd9Sstevel@tonic-gate MAP_TEXT | MAP_INITDATA)) != 0) { 6907c478bd9Sstevel@tonic-gate /* | MAP_RENAME */ /* not implemented, let user know */ 6917c478bd9Sstevel@tonic-gate return (EINVAL); 6927c478bd9Sstevel@tonic-gate } 6937c478bd9Sstevel@tonic-gate 6947c478bd9Sstevel@tonic-gate if ((flags & MAP_TEXT) && !(prot & PROT_EXEC)) { 6957c478bd9Sstevel@tonic-gate return (EINVAL); 6967c478bd9Sstevel@tonic-gate } 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate if ((flags & (MAP_TEXT | MAP_INITDATA)) == (MAP_TEXT | MAP_INITDATA)) { 6997c478bd9Sstevel@tonic-gate return (EINVAL); 7007c478bd9Sstevel@tonic-gate } 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate #if defined(__sparc) 7037c478bd9Sstevel@tonic-gate /* 7047c478bd9Sstevel@tonic-gate * See if this is an "old mmap call". If so, remember this 7057c478bd9Sstevel@tonic-gate * fact and convert the flags value given to mmap to indicate 7067c478bd9Sstevel@tonic-gate * the specified address in the system call must be used. 7077c478bd9Sstevel@tonic-gate * _MAP_NEW is turned set by all new uses of mmap. 7087c478bd9Sstevel@tonic-gate */ 7097c478bd9Sstevel@tonic-gate if ((flags & _MAP_NEW) == 0) 7107c478bd9Sstevel@tonic-gate flags |= MAP_FIXED; 7117c478bd9Sstevel@tonic-gate #endif 7127c478bd9Sstevel@tonic-gate flags &= ~_MAP_NEW; 7137c478bd9Sstevel@tonic-gate 7147c478bd9Sstevel@tonic-gate type = flags & MAP_TYPE; 7157c478bd9Sstevel@tonic-gate if (type != MAP_PRIVATE && type != MAP_SHARED) 7167c478bd9Sstevel@tonic-gate return (EINVAL); 7177c478bd9Sstevel@tonic-gate 7187c478bd9Sstevel@tonic-gate 7197c478bd9Sstevel@tonic-gate if (flags & MAP_ALIGN) { 7207c478bd9Sstevel@tonic-gate 7217c478bd9Sstevel@tonic-gate if (flags & MAP_FIXED) 7227c478bd9Sstevel@tonic-gate return (EINVAL); 7237c478bd9Sstevel@tonic-gate 7247c478bd9Sstevel@tonic-gate /* alignment needs to be a power of 2 >= page size */ 7257c478bd9Sstevel@tonic-gate if (((uintptr_t)*addrp < PAGESIZE && (uintptr_t)*addrp != 0) || 7267c478bd9Sstevel@tonic-gate !ISP2((uintptr_t)*addrp)) 7277c478bd9Sstevel@tonic-gate return (EINVAL); 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate /* 7307c478bd9Sstevel@tonic-gate * Check for bad lengths and file position. 7317c478bd9Sstevel@tonic-gate * We let the VOP_MAP routine check for negative lengths 7327c478bd9Sstevel@tonic-gate * since on some vnode types this might be appropriate. 7337c478bd9Sstevel@tonic-gate */ 7347c478bd9Sstevel@tonic-gate if (len == 0 || (pos & (u_offset_t)PAGEOFFSET) != 0) 7357c478bd9Sstevel@tonic-gate return (EINVAL); 7367c478bd9Sstevel@tonic-gate 7377c478bd9Sstevel@tonic-gate maxprot = PROT_ALL; /* start out allowing all accesses */ 7387c478bd9Sstevel@tonic-gate uprot = prot | PROT_USER; 7397c478bd9Sstevel@tonic-gate 7407c478bd9Sstevel@tonic-gate if (fp == NULL) { 7417c478bd9Sstevel@tonic-gate ASSERT(flags & MAP_ANON); 7427c478bd9Sstevel@tonic-gate as_rangelock(as); 7437c478bd9Sstevel@tonic-gate error = zmap(as, addrp, len, uprot, flags, pos); 7447c478bd9Sstevel@tonic-gate as_rangeunlock(as); 7457c478bd9Sstevel@tonic-gate return (error); 7467c478bd9Sstevel@tonic-gate } else if ((flags & MAP_ANON) != 0) 7477c478bd9Sstevel@tonic-gate return (EINVAL); 7487c478bd9Sstevel@tonic-gate 7497c478bd9Sstevel@tonic-gate vp = fp->f_vnode; 7507c478bd9Sstevel@tonic-gate 7517c478bd9Sstevel@tonic-gate /* Can't execute code from "noexec" mounted filesystem. */ 7527c478bd9Sstevel@tonic-gate if ((vp->v_vfsp->vfs_flag & VFS_NOEXEC) != 0) 7537c478bd9Sstevel@tonic-gate maxprot &= ~PROT_EXEC; 7547c478bd9Sstevel@tonic-gate 7557c478bd9Sstevel@tonic-gate /* 7567c478bd9Sstevel@tonic-gate * These checks were added as part of large files. 7577c478bd9Sstevel@tonic-gate * 75849a63d68Speterte * Return ENXIO if the initial position is negative; return EOVERFLOW 7597c478bd9Sstevel@tonic-gate * if (offset + len) would overflow the maximum allowed offset for the 7607c478bd9Sstevel@tonic-gate * type of file descriptor being used. 7617c478bd9Sstevel@tonic-gate */ 7627c478bd9Sstevel@tonic-gate if (vp->v_type == VREG) { 76349a63d68Speterte if (pos < 0) 76449a63d68Speterte return (ENXIO); 7657c478bd9Sstevel@tonic-gate if ((offset_t)len > (OFFSET_MAX(fp) - pos)) 7667c478bd9Sstevel@tonic-gate return (EOVERFLOW); 7677c478bd9Sstevel@tonic-gate } 7687c478bd9Sstevel@tonic-gate 7697c478bd9Sstevel@tonic-gate if (type == MAP_SHARED && (fp->f_flag & FWRITE) == 0) { 7707c478bd9Sstevel@tonic-gate /* no write access allowed */ 7717c478bd9Sstevel@tonic-gate maxprot &= ~PROT_WRITE; 7727c478bd9Sstevel@tonic-gate } 7737c478bd9Sstevel@tonic-gate 7747c478bd9Sstevel@tonic-gate /* 7757c478bd9Sstevel@tonic-gate * XXX - Do we also adjust maxprot based on protections 7767c478bd9Sstevel@tonic-gate * of the vnode? E.g. if no execute permission is given 7777c478bd9Sstevel@tonic-gate * on the vnode for the current user, maxprot probably 7787c478bd9Sstevel@tonic-gate * should disallow PROT_EXEC also? This is different 7797c478bd9Sstevel@tonic-gate * from the write access as this would be a per vnode 7807c478bd9Sstevel@tonic-gate * test as opposed to a per fd test for writability. 7817c478bd9Sstevel@tonic-gate */ 7827c478bd9Sstevel@tonic-gate 7837c478bd9Sstevel@tonic-gate /* 7847c478bd9Sstevel@tonic-gate * Verify that the specified protections are not greater than 7857c478bd9Sstevel@tonic-gate * the maximum allowable protections. Also test to make sure 7867c478bd9Sstevel@tonic-gate * that the file descriptor does allows for read access since 7877c478bd9Sstevel@tonic-gate * "write only" mappings are hard to do since normally we do 7887c478bd9Sstevel@tonic-gate * the read from the file before the page can be written. 7897c478bd9Sstevel@tonic-gate */ 7907c478bd9Sstevel@tonic-gate if (((maxprot & uprot) != uprot) || (fp->f_flag & FREAD) == 0) 7917c478bd9Sstevel@tonic-gate return (EACCES); 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate /* 7947c478bd9Sstevel@tonic-gate * If the user specified an address, do some simple checks here 7957c478bd9Sstevel@tonic-gate */ 7967c478bd9Sstevel@tonic-gate if ((flags & MAP_FIXED) != 0) { 7977c478bd9Sstevel@tonic-gate caddr_t userlimit; 7987c478bd9Sstevel@tonic-gate 7997c478bd9Sstevel@tonic-gate /* 8007c478bd9Sstevel@tonic-gate * Use the user address. First verify that 8017c478bd9Sstevel@tonic-gate * the address to be used is page aligned. 8027c478bd9Sstevel@tonic-gate * Then make some simple bounds checks. 8037c478bd9Sstevel@tonic-gate */ 8047c478bd9Sstevel@tonic-gate if (((uintptr_t)*addrp & PAGEOFFSET) != 0) 8057c478bd9Sstevel@tonic-gate return (EINVAL); 8067c478bd9Sstevel@tonic-gate 8077c478bd9Sstevel@tonic-gate userlimit = flags & _MAP_LOW32 ? 8087c478bd9Sstevel@tonic-gate (caddr_t)USERLIMIT32 : as->a_userlimit; 8097c478bd9Sstevel@tonic-gate switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) { 8107c478bd9Sstevel@tonic-gate case RANGE_OKAY: 8117c478bd9Sstevel@tonic-gate break; 8127c478bd9Sstevel@tonic-gate case RANGE_BADPROT: 8137c478bd9Sstevel@tonic-gate return (ENOTSUP); 8147c478bd9Sstevel@tonic-gate case RANGE_BADADDR: 8157c478bd9Sstevel@tonic-gate default: 8167c478bd9Sstevel@tonic-gate return (ENOMEM); 8177c478bd9Sstevel@tonic-gate } 8187c478bd9Sstevel@tonic-gate } 8197c478bd9Sstevel@tonic-gate 8207c478bd9Sstevel@tonic-gate 8217c478bd9Sstevel@tonic-gate /* 8227c478bd9Sstevel@tonic-gate * Ok, now let the vnode map routine do its thing to set things up. 8237c478bd9Sstevel@tonic-gate */ 8247c478bd9Sstevel@tonic-gate error = VOP_MAP(vp, pos, as, 8257c478bd9Sstevel@tonic-gate addrp, len, uprot, maxprot, flags, fp->f_cred); 8267c478bd9Sstevel@tonic-gate 8277c478bd9Sstevel@tonic-gate if (error == 0) { 8287c478bd9Sstevel@tonic-gate if (vp->v_type == VREG && 8297c478bd9Sstevel@tonic-gate (flags & (MAP_TEXT | MAP_INITDATA)) != 0) { 8307c478bd9Sstevel@tonic-gate /* 8317c478bd9Sstevel@tonic-gate * Mark this as an executable vnode 8327c478bd9Sstevel@tonic-gate */ 8337c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 8347c478bd9Sstevel@tonic-gate vp->v_flag |= VVMEXEC; 8357c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 8367c478bd9Sstevel@tonic-gate } 8377c478bd9Sstevel@tonic-gate } 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate return (error); 8407c478bd9Sstevel@tonic-gate } 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate #ifdef _LP64 8437c478bd9Sstevel@tonic-gate /* 8447c478bd9Sstevel@tonic-gate * LP64 mmap(2) system call: 64-bit offset, 64-bit address. 8457c478bd9Sstevel@tonic-gate * 8467c478bd9Sstevel@tonic-gate * The "large file" mmap routine mmap64(2) is also mapped to this routine 8477c478bd9Sstevel@tonic-gate * by the 64-bit version of libc. 8487c478bd9Sstevel@tonic-gate * 8497c478bd9Sstevel@tonic-gate * Eventually, this should be the only version, and have smmap_common() 8507c478bd9Sstevel@tonic-gate * folded back into it again. Some day. 8517c478bd9Sstevel@tonic-gate */ 8527c478bd9Sstevel@tonic-gate caddr_t 8537c478bd9Sstevel@tonic-gate smmap64(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos) 8547c478bd9Sstevel@tonic-gate { 8557c478bd9Sstevel@tonic-gate struct file *fp; 8567c478bd9Sstevel@tonic-gate int error; 8577c478bd9Sstevel@tonic-gate 8587c478bd9Sstevel@tonic-gate if (flags & _MAP_LOW32) 8597c478bd9Sstevel@tonic-gate error = EINVAL; 8607c478bd9Sstevel@tonic-gate else if (fd == -1 && (flags & MAP_ANON) != 0) 8617c478bd9Sstevel@tonic-gate error = smmap_common(&addr, len, prot, flags, 8627c478bd9Sstevel@tonic-gate NULL, (offset_t)pos); 8637c478bd9Sstevel@tonic-gate else if ((fp = getf(fd)) != NULL) { 8647c478bd9Sstevel@tonic-gate error = smmap_common(&addr, len, prot, flags, 8657c478bd9Sstevel@tonic-gate fp, (offset_t)pos); 8667c478bd9Sstevel@tonic-gate releasef(fd); 8677c478bd9Sstevel@tonic-gate } else 8687c478bd9Sstevel@tonic-gate error = EBADF; 8697c478bd9Sstevel@tonic-gate 8707c478bd9Sstevel@tonic-gate return (error ? (caddr_t)(uintptr_t)set_errno(error) : addr); 8717c478bd9Sstevel@tonic-gate } 8727c478bd9Sstevel@tonic-gate #endif /* _LP64 */ 8737c478bd9Sstevel@tonic-gate 8747c478bd9Sstevel@tonic-gate #if defined(_SYSCALL32_IMPL) || defined(_ILP32) 8757c478bd9Sstevel@tonic-gate 8767c478bd9Sstevel@tonic-gate /* 8777c478bd9Sstevel@tonic-gate * ILP32 mmap(2) system call: 32-bit offset, 32-bit address. 8787c478bd9Sstevel@tonic-gate */ 8797c478bd9Sstevel@tonic-gate caddr_t 8807c478bd9Sstevel@tonic-gate smmap32(caddr32_t addr, size32_t len, int prot, int flags, int fd, off32_t pos) 8817c478bd9Sstevel@tonic-gate { 8827c478bd9Sstevel@tonic-gate struct file *fp; 8837c478bd9Sstevel@tonic-gate int error; 8847c478bd9Sstevel@tonic-gate caddr_t a = (caddr_t)(uintptr_t)addr; 8857c478bd9Sstevel@tonic-gate 8867c478bd9Sstevel@tonic-gate if (flags & _MAP_LOW32) 8877c478bd9Sstevel@tonic-gate error = EINVAL; 8887c478bd9Sstevel@tonic-gate else if (fd == -1 && (flags & MAP_ANON) != 0) 8897c478bd9Sstevel@tonic-gate error = smmap_common(&a, (size_t)len, prot, 8907c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, NULL, (offset_t)pos); 8917c478bd9Sstevel@tonic-gate else if ((fp = getf(fd)) != NULL) { 8927c478bd9Sstevel@tonic-gate error = smmap_common(&a, (size_t)len, prot, 8937c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, fp, (offset_t)pos); 8947c478bd9Sstevel@tonic-gate releasef(fd); 8957c478bd9Sstevel@tonic-gate } else 8967c478bd9Sstevel@tonic-gate error = EBADF; 8977c478bd9Sstevel@tonic-gate 8987c478bd9Sstevel@tonic-gate ASSERT(error != 0 || (uintptr_t)(a + len) < (uintptr_t)UINT32_MAX); 8997c478bd9Sstevel@tonic-gate 9007c478bd9Sstevel@tonic-gate return (error ? (caddr_t)(uintptr_t)set_errno(error) : a); 9017c478bd9Sstevel@tonic-gate } 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate /* 9047c478bd9Sstevel@tonic-gate * ILP32 mmap64(2) system call: 64-bit offset, 32-bit address. 9057c478bd9Sstevel@tonic-gate * 9067c478bd9Sstevel@tonic-gate * Now things really get ugly because we can't use the C-style 9077c478bd9Sstevel@tonic-gate * calling convention for more than 6 args, and 64-bit parameter 9087c478bd9Sstevel@tonic-gate * passing on 32-bit systems is less than clean. 9097c478bd9Sstevel@tonic-gate */ 9107c478bd9Sstevel@tonic-gate 9117c478bd9Sstevel@tonic-gate struct mmaplf32a { 9127c478bd9Sstevel@tonic-gate caddr_t addr; 9137c478bd9Sstevel@tonic-gate size_t len; 9147c478bd9Sstevel@tonic-gate #ifdef _LP64 9157c478bd9Sstevel@tonic-gate /* 9167c478bd9Sstevel@tonic-gate * 32-bit contents, 64-bit cells 9177c478bd9Sstevel@tonic-gate */ 9187c478bd9Sstevel@tonic-gate uint64_t prot; 9197c478bd9Sstevel@tonic-gate uint64_t flags; 9207c478bd9Sstevel@tonic-gate uint64_t fd; 9217c478bd9Sstevel@tonic-gate uint64_t offhi; 9227c478bd9Sstevel@tonic-gate uint64_t offlo; 9237c478bd9Sstevel@tonic-gate #else 9247c478bd9Sstevel@tonic-gate /* 9257c478bd9Sstevel@tonic-gate * 32-bit contents, 32-bit cells 9267c478bd9Sstevel@tonic-gate */ 9277c478bd9Sstevel@tonic-gate uint32_t prot; 9287c478bd9Sstevel@tonic-gate uint32_t flags; 9297c478bd9Sstevel@tonic-gate uint32_t fd; 9307c478bd9Sstevel@tonic-gate uint32_t offhi; 9317c478bd9Sstevel@tonic-gate uint32_t offlo; 9327c478bd9Sstevel@tonic-gate #endif 9337c478bd9Sstevel@tonic-gate }; 9347c478bd9Sstevel@tonic-gate 9357c478bd9Sstevel@tonic-gate int 9367c478bd9Sstevel@tonic-gate smmaplf32(struct mmaplf32a *uap, rval_t *rvp) 9377c478bd9Sstevel@tonic-gate { 9387c478bd9Sstevel@tonic-gate struct file *fp; 9397c478bd9Sstevel@tonic-gate int error; 9407c478bd9Sstevel@tonic-gate caddr_t a = uap->addr; 9417c478bd9Sstevel@tonic-gate int flags = (int)uap->flags; 9427c478bd9Sstevel@tonic-gate int fd = (int)uap->fd; 9437c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 9447c478bd9Sstevel@tonic-gate offset_t off = ((u_offset_t)uap->offhi << 32) | (u_offset_t)uap->offlo; 9457c478bd9Sstevel@tonic-gate #else 9467c478bd9Sstevel@tonic-gate offset_t off = ((u_offset_t)uap->offlo << 32) | (u_offset_t)uap->offhi; 9477c478bd9Sstevel@tonic-gate #endif 9487c478bd9Sstevel@tonic-gate 9497c478bd9Sstevel@tonic-gate if (flags & _MAP_LOW32) 9507c478bd9Sstevel@tonic-gate error = EINVAL; 9517c478bd9Sstevel@tonic-gate else if (fd == -1 && (flags & MAP_ANON) != 0) 9527c478bd9Sstevel@tonic-gate error = smmap_common(&a, uap->len, (int)uap->prot, 9537c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, NULL, off); 9547c478bd9Sstevel@tonic-gate else if ((fp = getf(fd)) != NULL) { 9557c478bd9Sstevel@tonic-gate error = smmap_common(&a, uap->len, (int)uap->prot, 9567c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, fp, off); 9577c478bd9Sstevel@tonic-gate releasef(fd); 9587c478bd9Sstevel@tonic-gate } else 9597c478bd9Sstevel@tonic-gate error = EBADF; 9607c478bd9Sstevel@tonic-gate 9617c478bd9Sstevel@tonic-gate if (error == 0) 9627c478bd9Sstevel@tonic-gate rvp->r_val1 = (uintptr_t)a; 9637c478bd9Sstevel@tonic-gate return (error); 9647c478bd9Sstevel@tonic-gate } 9657c478bd9Sstevel@tonic-gate 9667c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL || _ILP32 */ 9677c478bd9Sstevel@tonic-gate 9687c478bd9Sstevel@tonic-gate int 9697c478bd9Sstevel@tonic-gate munmap(caddr_t addr, size_t len) 9707c478bd9Sstevel@tonic-gate { 9717c478bd9Sstevel@tonic-gate struct proc *p = curproc; 9727c478bd9Sstevel@tonic-gate struct as *as = p->p_as; 9737c478bd9Sstevel@tonic-gate 9747c478bd9Sstevel@tonic-gate if (((uintptr_t)addr & PAGEOFFSET) != 0 || len == 0) 9757c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 9767c478bd9Sstevel@tonic-gate 9777c478bd9Sstevel@tonic-gate if (valid_usr_range(addr, len, 0, as, as->a_userlimit) != RANGE_OKAY) 9787c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 9797c478bd9Sstevel@tonic-gate 9807c478bd9Sstevel@tonic-gate /* 9817c478bd9Sstevel@tonic-gate * Discard lwpchan mappings. 9827c478bd9Sstevel@tonic-gate */ 9837c478bd9Sstevel@tonic-gate if (p->p_lcp != NULL) 9847c478bd9Sstevel@tonic-gate lwpchan_delete_mapping(p, addr, addr + len); 9857c478bd9Sstevel@tonic-gate if (as_unmap(as, addr, len) != 0) 9867c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 9877c478bd9Sstevel@tonic-gate 9887c478bd9Sstevel@tonic-gate return (0); 9897c478bd9Sstevel@tonic-gate } 9907c478bd9Sstevel@tonic-gate 9917c478bd9Sstevel@tonic-gate int 9927c478bd9Sstevel@tonic-gate mprotect(caddr_t addr, size_t len, int prot) 9937c478bd9Sstevel@tonic-gate { 9947c478bd9Sstevel@tonic-gate struct as *as = curproc->p_as; 9957c478bd9Sstevel@tonic-gate uint_t uprot = prot | PROT_USER; 9967c478bd9Sstevel@tonic-gate int error; 9977c478bd9Sstevel@tonic-gate 9987c478bd9Sstevel@tonic-gate if (((uintptr_t)addr & PAGEOFFSET) != 0 || len == 0) 9997c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 10007c478bd9Sstevel@tonic-gate 10017c478bd9Sstevel@tonic-gate switch (valid_usr_range(addr, len, prot, as, as->a_userlimit)) { 10027c478bd9Sstevel@tonic-gate case RANGE_OKAY: 10037c478bd9Sstevel@tonic-gate break; 10047c478bd9Sstevel@tonic-gate case RANGE_BADPROT: 10057c478bd9Sstevel@tonic-gate return (set_errno(ENOTSUP)); 10067c478bd9Sstevel@tonic-gate case RANGE_BADADDR: 10077c478bd9Sstevel@tonic-gate default: 10087c478bd9Sstevel@tonic-gate return (set_errno(ENOMEM)); 10097c478bd9Sstevel@tonic-gate } 10107c478bd9Sstevel@tonic-gate 10117c478bd9Sstevel@tonic-gate error = as_setprot(as, addr, len, uprot); 10127c478bd9Sstevel@tonic-gate if (error) 10137c478bd9Sstevel@tonic-gate return (set_errno(error)); 10147c478bd9Sstevel@tonic-gate return (0); 10157c478bd9Sstevel@tonic-gate } 10167c478bd9Sstevel@tonic-gate 10177c478bd9Sstevel@tonic-gate #define MC_CACHE 128 /* internal result buffer */ 10187c478bd9Sstevel@tonic-gate #define MC_QUANTUM (MC_CACHE * PAGESIZE) /* addresses covered in loop */ 10197c478bd9Sstevel@tonic-gate 10207c478bd9Sstevel@tonic-gate int 10217c478bd9Sstevel@tonic-gate mincore(caddr_t addr, size_t len, char *vecp) 10227c478bd9Sstevel@tonic-gate { 10237c478bd9Sstevel@tonic-gate struct as *as = curproc->p_as; 10247c478bd9Sstevel@tonic-gate caddr_t ea; /* end address of loop */ 10257c478bd9Sstevel@tonic-gate size_t rl; /* inner result length */ 10267c478bd9Sstevel@tonic-gate char vec[MC_CACHE]; /* local vector cache */ 10277c478bd9Sstevel@tonic-gate int error; 10287c478bd9Sstevel@tonic-gate model_t model; 10297c478bd9Sstevel@tonic-gate long llen; 10307c478bd9Sstevel@tonic-gate 10317c478bd9Sstevel@tonic-gate model = get_udatamodel(); 10327c478bd9Sstevel@tonic-gate /* 10337c478bd9Sstevel@tonic-gate * Validate form of address parameters. 10347c478bd9Sstevel@tonic-gate */ 10357c478bd9Sstevel@tonic-gate if (model == DATAMODEL_NATIVE) { 10367c478bd9Sstevel@tonic-gate llen = (long)len; 10377c478bd9Sstevel@tonic-gate } else { 10387c478bd9Sstevel@tonic-gate llen = (int32_t)(size32_t)len; 10397c478bd9Sstevel@tonic-gate } 10407c478bd9Sstevel@tonic-gate if (((uintptr_t)addr & PAGEOFFSET) != 0 || llen <= 0) 10417c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 10427c478bd9Sstevel@tonic-gate 10437c478bd9Sstevel@tonic-gate if (valid_usr_range(addr, len, 0, as, as->a_userlimit) != RANGE_OKAY) 10447c478bd9Sstevel@tonic-gate return (set_errno(ENOMEM)); 10457c478bd9Sstevel@tonic-gate 10467c478bd9Sstevel@tonic-gate /* 10477c478bd9Sstevel@tonic-gate * Loop over subranges of interval [addr : addr + len), recovering 10487c478bd9Sstevel@tonic-gate * results internally and then copying them out to caller. Subrange 10497c478bd9Sstevel@tonic-gate * is based on the size of MC_CACHE, defined above. 10507c478bd9Sstevel@tonic-gate */ 10517c478bd9Sstevel@tonic-gate for (ea = addr + len; addr < ea; addr += MC_QUANTUM) { 10527c478bd9Sstevel@tonic-gate error = as_incore(as, addr, 10537c478bd9Sstevel@tonic-gate (size_t)MIN(MC_QUANTUM, ea - addr), vec, &rl); 10547c478bd9Sstevel@tonic-gate if (rl != 0) { 10557c478bd9Sstevel@tonic-gate rl = (rl + PAGESIZE - 1) / PAGESIZE; 10567c478bd9Sstevel@tonic-gate if (copyout(vec, vecp, rl) != 0) 10577c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 10587c478bd9Sstevel@tonic-gate vecp += rl; 10597c478bd9Sstevel@tonic-gate } 10607c478bd9Sstevel@tonic-gate if (error != 0) 10617c478bd9Sstevel@tonic-gate return (set_errno(ENOMEM)); 10627c478bd9Sstevel@tonic-gate } 10637c478bd9Sstevel@tonic-gate return (0); 10647c478bd9Sstevel@tonic-gate } 1065