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 507b65a64Saguzovsk * Common Development and Distribution License (the "License"). 607b65a64Saguzovsk * 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 */ 21783f4f5eSRoger A. Faulkner 221b3b16f3STheo Schlossnagle /* Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved. */ 231b3b16f3STheo Schlossnagle 247c478bd9Sstevel@tonic-gate /* 25783f4f5eSRoger A. Faulkner * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 267c478bd9Sstevel@tonic-gate * Use is subject to license terms. 277c478bd9Sstevel@tonic-gate */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 307c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 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> 54da6c28aaSamw #include <sys/nbmlock.h> 557c478bd9Sstevel@tonic-gate 567c478bd9Sstevel@tonic-gate #include <vm/hat.h> 577c478bd9Sstevel@tonic-gate #include <vm/as.h> 587c478bd9Sstevel@tonic-gate #include <vm/seg.h> 597c478bd9Sstevel@tonic-gate #include <vm/seg_dev.h> 607c478bd9Sstevel@tonic-gate #include <vm/seg_vn.h> 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate int use_brk_lpg = 1; 637c478bd9Sstevel@tonic-gate int use_stk_lpg = 1; 647c478bd9Sstevel@tonic-gate 65*d2a70789SRichard Lowe /* 66*d2a70789SRichard Lowe * If set, we will not randomize mappings where the 'addr' argument is 67*d2a70789SRichard Lowe * non-NULL and not an alignment. 68*d2a70789SRichard Lowe */ 69*d2a70789SRichard Lowe int aslr_respect_mmap_hint = 1; 70*d2a70789SRichard Lowe 717c478bd9Sstevel@tonic-gate static int brk_lpg(caddr_t nva); 727c478bd9Sstevel@tonic-gate static int grow_lpg(caddr_t sp); 737c478bd9Sstevel@tonic-gate 74*d2a70789SRichard Lowe intptr_t 757c478bd9Sstevel@tonic-gate brk(caddr_t nva) 767c478bd9Sstevel@tonic-gate { 777c478bd9Sstevel@tonic-gate int error; 787c478bd9Sstevel@tonic-gate proc_t *p = curproc; 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate /* 817c478bd9Sstevel@tonic-gate * Serialize brk operations on an address space. 827c478bd9Sstevel@tonic-gate * This also serves as the lock protecting p_brksize 837c478bd9Sstevel@tonic-gate * and p_brkpageszc. 847c478bd9Sstevel@tonic-gate */ 857c478bd9Sstevel@tonic-gate as_rangelock(p->p_as); 86*d2a70789SRichard Lowe 87*d2a70789SRichard Lowe /* 88*d2a70789SRichard Lowe * As a special case to aid the implementation of sbrk(3C), if given a 89*d2a70789SRichard Lowe * new brk of 0, return the current brk. We'll hide this in brk(3C). 90*d2a70789SRichard Lowe */ 91*d2a70789SRichard Lowe if (nva == 0) { 92*d2a70789SRichard Lowe intptr_t base = (intptr_t)(p->p_brkbase + p->p_brksize); 93*d2a70789SRichard Lowe as_rangeunlock(p->p_as); 94*d2a70789SRichard Lowe return (base); 95*d2a70789SRichard Lowe } 96*d2a70789SRichard Lowe 977c478bd9Sstevel@tonic-gate if (use_brk_lpg && (p->p_flag & SAUTOLPG) != 0) { 987c478bd9Sstevel@tonic-gate error = brk_lpg(nva); 997c478bd9Sstevel@tonic-gate } else { 1007c478bd9Sstevel@tonic-gate error = brk_internal(nva, p->p_brkpageszc); 1017c478bd9Sstevel@tonic-gate } 1027c478bd9Sstevel@tonic-gate as_rangeunlock(p->p_as); 1037c478bd9Sstevel@tonic-gate return ((error != 0 ? set_errno(error) : 0)); 1047c478bd9Sstevel@tonic-gate } 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate /* 1077c478bd9Sstevel@tonic-gate * Algorithm: call arch-specific map_pgsz to get best page size to use, 1087c478bd9Sstevel@tonic-gate * then call brk_internal(). 1097c478bd9Sstevel@tonic-gate * Returns 0 on success. 1107c478bd9Sstevel@tonic-gate */ 1117c478bd9Sstevel@tonic-gate static int 1127c478bd9Sstevel@tonic-gate brk_lpg(caddr_t nva) 1137c478bd9Sstevel@tonic-gate { 1147c478bd9Sstevel@tonic-gate struct proc *p = curproc; 1157c478bd9Sstevel@tonic-gate size_t pgsz, len; 116ec25b48fSsusans caddr_t addr, brkend; 1177c478bd9Sstevel@tonic-gate caddr_t bssbase = p->p_bssbase; 1187c478bd9Sstevel@tonic-gate caddr_t brkbase = p->p_brkbase; 1197c478bd9Sstevel@tonic-gate int oszc, szc; 1207c478bd9Sstevel@tonic-gate int err; 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate oszc = p->p_brkpageszc; 1237c478bd9Sstevel@tonic-gate 1247c478bd9Sstevel@tonic-gate /* 1257c478bd9Sstevel@tonic-gate * If p_brkbase has not yet been set, the first call 1267c478bd9Sstevel@tonic-gate * to brk_internal() will initialize it. 1277c478bd9Sstevel@tonic-gate */ 1287c478bd9Sstevel@tonic-gate if (brkbase == 0) { 1297c478bd9Sstevel@tonic-gate return (brk_internal(nva, oszc)); 1307c478bd9Sstevel@tonic-gate } 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate len = nva - bssbase; 1337c478bd9Sstevel@tonic-gate 134ec25b48fSsusans pgsz = map_pgsz(MAPPGSZ_HEAP, p, bssbase, len, 0); 1357c478bd9Sstevel@tonic-gate szc = page_szc(pgsz); 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate /* 1387c478bd9Sstevel@tonic-gate * Covers two cases: 1397c478bd9Sstevel@tonic-gate * 1. page_szc() returns -1 for invalid page size, so we want to 1407c478bd9Sstevel@tonic-gate * ignore it in that case. 1417c478bd9Sstevel@tonic-gate * 2. By design we never decrease page size, as it is more stable. 1427c478bd9Sstevel@tonic-gate */ 1437c478bd9Sstevel@tonic-gate if (szc <= oszc) { 1447c478bd9Sstevel@tonic-gate err = brk_internal(nva, oszc); 1457c478bd9Sstevel@tonic-gate /* If failed, back off to base page size. */ 1467c478bd9Sstevel@tonic-gate if (err != 0 && oszc != 0) { 1477c478bd9Sstevel@tonic-gate err = brk_internal(nva, 0); 1487c478bd9Sstevel@tonic-gate } 1497c478bd9Sstevel@tonic-gate return (err); 1507c478bd9Sstevel@tonic-gate } 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate err = brk_internal(nva, szc); 1537c478bd9Sstevel@tonic-gate /* If using szc failed, map with base page size and return. */ 1547c478bd9Sstevel@tonic-gate if (err != 0) { 1557c478bd9Sstevel@tonic-gate if (szc != 0) { 1567c478bd9Sstevel@tonic-gate err = brk_internal(nva, 0); 1577c478bd9Sstevel@tonic-gate } 1587c478bd9Sstevel@tonic-gate return (err); 1597c478bd9Sstevel@tonic-gate } 1607c478bd9Sstevel@tonic-gate 161ec25b48fSsusans /* 162ec25b48fSsusans * Round up brk base to a large page boundary and remap 163ec25b48fSsusans * anything in the segment already faulted in beyond that 164ec25b48fSsusans * point. 165ec25b48fSsusans */ 166ec25b48fSsusans addr = (caddr_t)P2ROUNDUP((uintptr_t)p->p_bssbase, pgsz); 167ec25b48fSsusans brkend = brkbase + p->p_brksize; 168ec25b48fSsusans len = brkend - addr; 169ec25b48fSsusans /* Check that len is not negative. Update page size code for heap. */ 170ec25b48fSsusans if (addr >= p->p_bssbase && brkend > addr && IS_P2ALIGNED(len, pgsz)) { 1717c478bd9Sstevel@tonic-gate (void) as_setpagesize(p->p_as, addr, len, szc, B_FALSE); 172ec25b48fSsusans p->p_brkpageszc = szc; 1737c478bd9Sstevel@tonic-gate } 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate ASSERT(err == 0); 1767c478bd9Sstevel@tonic-gate return (err); /* should always be 0 */ 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate /* 1807c478bd9Sstevel@tonic-gate * Returns 0 on success. 1817c478bd9Sstevel@tonic-gate */ 1827c478bd9Sstevel@tonic-gate int 1837c478bd9Sstevel@tonic-gate brk_internal(caddr_t nva, uint_t brkszc) 1847c478bd9Sstevel@tonic-gate { 1857c478bd9Sstevel@tonic-gate caddr_t ova; /* current break address */ 1867c478bd9Sstevel@tonic-gate size_t size; 1877c478bd9Sstevel@tonic-gate int error; 1887c478bd9Sstevel@tonic-gate struct proc *p = curproc; 1897c478bd9Sstevel@tonic-gate struct as *as = p->p_as; 1907c478bd9Sstevel@tonic-gate size_t pgsz; 1917c478bd9Sstevel@tonic-gate uint_t szc; 1927c478bd9Sstevel@tonic-gate rctl_qty_t as_rctl; 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate /* 1957c478bd9Sstevel@tonic-gate * extend heap to brkszc alignment but use current p->p_brkpageszc 1967c478bd9Sstevel@tonic-gate * for the newly created segment. This allows the new extension 1977c478bd9Sstevel@tonic-gate * segment to be concatenated successfully with the existing brk 1987c478bd9Sstevel@tonic-gate * segment. 1997c478bd9Sstevel@tonic-gate */ 2007c478bd9Sstevel@tonic-gate if ((szc = brkszc) != 0) { 2017c478bd9Sstevel@tonic-gate pgsz = page_get_pagesize(szc); 2027c478bd9Sstevel@tonic-gate ASSERT(pgsz > PAGESIZE); 2037c478bd9Sstevel@tonic-gate } else { 2047c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 2057c478bd9Sstevel@tonic-gate } 2067c478bd9Sstevel@tonic-gate 2077c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 2087c478bd9Sstevel@tonic-gate as_rctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_DATA], 2097c478bd9Sstevel@tonic-gate p->p_rctls, p); 2107c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 2117c478bd9Sstevel@tonic-gate 2127c478bd9Sstevel@tonic-gate /* 2137c478bd9Sstevel@tonic-gate * If p_brkbase has not yet been set, the first call 2147c478bd9Sstevel@tonic-gate * to brk() will initialize it. 2157c478bd9Sstevel@tonic-gate */ 2167c478bd9Sstevel@tonic-gate if (p->p_brkbase == 0) 2177c478bd9Sstevel@tonic-gate p->p_brkbase = nva; 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate /* 2207c478bd9Sstevel@tonic-gate * Before multiple page size support existed p_brksize was the value 2217c478bd9Sstevel@tonic-gate * not rounded to the pagesize (i.e. it stored the exact user request 2227c478bd9Sstevel@tonic-gate * for heap size). If pgsz is greater than PAGESIZE calculate the 2237c478bd9Sstevel@tonic-gate * heap size as the real new heap size by rounding it up to pgsz. 2247c478bd9Sstevel@tonic-gate * This is useful since we may want to know where the heap ends 2257c478bd9Sstevel@tonic-gate * without knowing heap pagesize (e.g. some old code) and also if 2267c478bd9Sstevel@tonic-gate * heap pagesize changes we can update p_brkpageszc but delay adding 2277c478bd9Sstevel@tonic-gate * new mapping yet still know from p_brksize where the heap really 2287c478bd9Sstevel@tonic-gate * ends. The user requested heap end is stored in libc variable. 2297c478bd9Sstevel@tonic-gate */ 2307c478bd9Sstevel@tonic-gate if (pgsz > PAGESIZE) { 2317c478bd9Sstevel@tonic-gate caddr_t tnva = (caddr_t)P2ROUNDUP((uintptr_t)nva, pgsz); 2327c478bd9Sstevel@tonic-gate size = tnva - p->p_brkbase; 2337c478bd9Sstevel@tonic-gate if (tnva < p->p_brkbase || (size > p->p_brksize && 2347c478bd9Sstevel@tonic-gate size > (size_t)as_rctl)) { 2357c478bd9Sstevel@tonic-gate szc = 0; 2367c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 2377c478bd9Sstevel@tonic-gate size = nva - p->p_brkbase; 2387c478bd9Sstevel@tonic-gate } 2397c478bd9Sstevel@tonic-gate } else { 2407c478bd9Sstevel@tonic-gate size = nva - p->p_brkbase; 2417c478bd9Sstevel@tonic-gate } 2427c478bd9Sstevel@tonic-gate 2437c478bd9Sstevel@tonic-gate /* 2447c478bd9Sstevel@tonic-gate * use PAGESIZE to roundup ova because we want to know the real value 2457c478bd9Sstevel@tonic-gate * of the current heap end in case p_brkpageszc changes since the last 2467c478bd9Sstevel@tonic-gate * p_brksize was computed. 2477c478bd9Sstevel@tonic-gate */ 2487c478bd9Sstevel@tonic-gate nva = (caddr_t)P2ROUNDUP((uintptr_t)nva, pgsz); 2497c478bd9Sstevel@tonic-gate ova = (caddr_t)P2ROUNDUP((uintptr_t)(p->p_brkbase + p->p_brksize), 25060946fe0Smec PAGESIZE); 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate if ((nva < p->p_brkbase) || (size > p->p_brksize && 2537c478bd9Sstevel@tonic-gate size > as_rctl)) { 2547c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 2557c478bd9Sstevel@tonic-gate (void) rctl_action(rctlproc_legacy[RLIMIT_DATA], p->p_rctls, p, 2567c478bd9Sstevel@tonic-gate RCA_SAFE); 2577c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 2587c478bd9Sstevel@tonic-gate return (ENOMEM); 2597c478bd9Sstevel@tonic-gate } 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate if (nva > ova) { 2627c478bd9Sstevel@tonic-gate struct segvn_crargs crargs = 2637c478bd9Sstevel@tonic-gate SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 2647c478bd9Sstevel@tonic-gate 2657c478bd9Sstevel@tonic-gate if (!(p->p_datprot & PROT_EXEC)) { 2667c478bd9Sstevel@tonic-gate crargs.prot &= ~PROT_EXEC; 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate /* 2707c478bd9Sstevel@tonic-gate * Add new zfod mapping to extend UNIX data segment 271ec25b48fSsusans * AS_MAP_NO_LPOOB means use 0, and don't reapply OOB policies 272ec25b48fSsusans * via map_pgszcvec(). Use AS_MAP_HEAP to get intermediate 273ec25b48fSsusans * page sizes if ova is not aligned to szc's pgsz. 2747c478bd9Sstevel@tonic-gate */ 275ec25b48fSsusans if (szc > 0) { 276ec25b48fSsusans caddr_t rbss; 277ec25b48fSsusans 278ec25b48fSsusans rbss = (caddr_t)P2ROUNDUP((uintptr_t)p->p_bssbase, 279ec25b48fSsusans pgsz); 280ec25b48fSsusans if (IS_P2ALIGNED(p->p_bssbase, pgsz) || ova > rbss) { 281ec25b48fSsusans crargs.szc = p->p_brkpageszc ? p->p_brkpageszc : 282ec25b48fSsusans AS_MAP_NO_LPOOB; 283ec25b48fSsusans } else if (ova == rbss) { 284ec25b48fSsusans crargs.szc = szc; 285ec25b48fSsusans } else { 286ec25b48fSsusans crargs.szc = AS_MAP_HEAP; 287ec25b48fSsusans } 288ec25b48fSsusans } else { 289ec25b48fSsusans crargs.szc = AS_MAP_NO_LPOOB; 290ec25b48fSsusans } 2917c478bd9Sstevel@tonic-gate crargs.lgrp_mem_policy_flags = LGRP_MP_FLAG_EXTEND_UP; 2927c478bd9Sstevel@tonic-gate error = as_map(as, ova, (size_t)(nva - ova), segvn_create, 2937c478bd9Sstevel@tonic-gate &crargs); 2947c478bd9Sstevel@tonic-gate if (error) { 2957c478bd9Sstevel@tonic-gate return (error); 2967c478bd9Sstevel@tonic-gate } 2977c478bd9Sstevel@tonic-gate 2987c478bd9Sstevel@tonic-gate } else if (nva < ova) { 2997c478bd9Sstevel@tonic-gate /* 3007c478bd9Sstevel@tonic-gate * Release mapping to shrink UNIX data segment. 3017c478bd9Sstevel@tonic-gate */ 3027c478bd9Sstevel@tonic-gate (void) as_unmap(as, nva, (size_t)(ova - nva)); 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate p->p_brksize = size; 3057c478bd9Sstevel@tonic-gate return (0); 3067c478bd9Sstevel@tonic-gate } 3077c478bd9Sstevel@tonic-gate 3087c478bd9Sstevel@tonic-gate /* 3097c478bd9Sstevel@tonic-gate * Grow the stack to include sp. Return 1 if successful, 0 otherwise. 3107c478bd9Sstevel@tonic-gate * This routine assumes that the stack grows downward. 3117c478bd9Sstevel@tonic-gate */ 3127c478bd9Sstevel@tonic-gate int 3137c478bd9Sstevel@tonic-gate grow(caddr_t sp) 3147c478bd9Sstevel@tonic-gate { 3157c478bd9Sstevel@tonic-gate struct proc *p = curproc; 316ec25b48fSsusans struct as *as = p->p_as; 317ec25b48fSsusans size_t oldsize = p->p_stksize; 318ec25b48fSsusans size_t newsize; 3197c478bd9Sstevel@tonic-gate int err; 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate /* 3227c478bd9Sstevel@tonic-gate * Serialize grow operations on an address space. 3237c478bd9Sstevel@tonic-gate * This also serves as the lock protecting p_stksize 3247c478bd9Sstevel@tonic-gate * and p_stkpageszc. 3257c478bd9Sstevel@tonic-gate */ 326ec25b48fSsusans as_rangelock(as); 3277c478bd9Sstevel@tonic-gate if (use_stk_lpg && (p->p_flag & SAUTOLPG) != 0) { 3287c478bd9Sstevel@tonic-gate err = grow_lpg(sp); 3297c478bd9Sstevel@tonic-gate } else { 3307c478bd9Sstevel@tonic-gate err = grow_internal(sp, p->p_stkpageszc); 3317c478bd9Sstevel@tonic-gate } 332ec25b48fSsusans as_rangeunlock(as); 333ec25b48fSsusans 334ec25b48fSsusans if (err == 0 && (newsize = p->p_stksize) > oldsize) { 335ec25b48fSsusans ASSERT(IS_P2ALIGNED(oldsize, PAGESIZE)); 336ec25b48fSsusans ASSERT(IS_P2ALIGNED(newsize, PAGESIZE)); 337ec25b48fSsusans /* 338ec25b48fSsusans * Set up translations so the process doesn't have to fault in 339ec25b48fSsusans * the stack pages we just gave it. 340ec25b48fSsusans */ 341ec25b48fSsusans (void) as_fault(as->a_hat, as, p->p_usrstack - newsize, 342ec25b48fSsusans newsize - oldsize, F_INVAL, S_WRITE); 343ec25b48fSsusans } 3447c478bd9Sstevel@tonic-gate return ((err == 0 ? 1 : 0)); 3457c478bd9Sstevel@tonic-gate } 3467c478bd9Sstevel@tonic-gate 3477c478bd9Sstevel@tonic-gate /* 3487c478bd9Sstevel@tonic-gate * Algorithm: call arch-specific map_pgsz to get best page size to use, 3497c478bd9Sstevel@tonic-gate * then call grow_internal(). 3507c478bd9Sstevel@tonic-gate * Returns 0 on success. 3517c478bd9Sstevel@tonic-gate */ 3527c478bd9Sstevel@tonic-gate static int 3537c478bd9Sstevel@tonic-gate grow_lpg(caddr_t sp) 3547c478bd9Sstevel@tonic-gate { 3557c478bd9Sstevel@tonic-gate struct proc *p = curproc; 3567c478bd9Sstevel@tonic-gate size_t pgsz; 3577c478bd9Sstevel@tonic-gate size_t len, newsize; 358ec25b48fSsusans caddr_t addr, saddr; 359ec25b48fSsusans caddr_t growend; 3607c478bd9Sstevel@tonic-gate int oszc, szc; 3617c478bd9Sstevel@tonic-gate int err; 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate newsize = p->p_usrstack - sp; 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate oszc = p->p_stkpageszc; 366ec25b48fSsusans pgsz = map_pgsz(MAPPGSZ_STK, p, sp, newsize, 0); 3677c478bd9Sstevel@tonic-gate szc = page_szc(pgsz); 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate /* 3707c478bd9Sstevel@tonic-gate * Covers two cases: 3717c478bd9Sstevel@tonic-gate * 1. page_szc() returns -1 for invalid page size, so we want to 3727c478bd9Sstevel@tonic-gate * ignore it in that case. 3737c478bd9Sstevel@tonic-gate * 2. By design we never decrease page size, as it is more stable. 3747c478bd9Sstevel@tonic-gate * This shouldn't happen as the stack never shrinks. 3757c478bd9Sstevel@tonic-gate */ 3767c478bd9Sstevel@tonic-gate if (szc <= oszc) { 3777c478bd9Sstevel@tonic-gate err = grow_internal(sp, oszc); 3787c478bd9Sstevel@tonic-gate /* failed, fall back to base page size */ 3797c478bd9Sstevel@tonic-gate if (err != 0 && oszc != 0) { 3807c478bd9Sstevel@tonic-gate err = grow_internal(sp, 0); 3817c478bd9Sstevel@tonic-gate } 3827c478bd9Sstevel@tonic-gate return (err); 3837c478bd9Sstevel@tonic-gate } 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate /* 3867c478bd9Sstevel@tonic-gate * We've grown sufficiently to switch to a new page size. 387ec25b48fSsusans * So we are going to remap the whole segment with the new page size. 3887c478bd9Sstevel@tonic-gate */ 3897c478bd9Sstevel@tonic-gate err = grow_internal(sp, szc); 3907c478bd9Sstevel@tonic-gate /* The grow with szc failed, so fall back to base page size. */ 3917c478bd9Sstevel@tonic-gate if (err != 0) { 3927c478bd9Sstevel@tonic-gate if (szc != 0) { 3937c478bd9Sstevel@tonic-gate err = grow_internal(sp, 0); 3947c478bd9Sstevel@tonic-gate } 3957c478bd9Sstevel@tonic-gate return (err); 3967c478bd9Sstevel@tonic-gate } 3977c478bd9Sstevel@tonic-gate 398ec25b48fSsusans /* 399ec25b48fSsusans * Round up stack pointer to a large page boundary and remap 400ec25b48fSsusans * any pgsz pages in the segment already faulted in beyond that 401ec25b48fSsusans * point. 402ec25b48fSsusans */ 403ec25b48fSsusans saddr = p->p_usrstack - p->p_stksize; 404ec25b48fSsusans addr = (caddr_t)P2ROUNDUP((uintptr_t)saddr, pgsz); 405ec25b48fSsusans growend = (caddr_t)P2ALIGN((uintptr_t)p->p_usrstack, pgsz); 406ec25b48fSsusans len = growend - addr; 407ec25b48fSsusans /* Check that len is not negative. Update page size code for stack. */ 408ec25b48fSsusans if (addr >= saddr && growend > addr && IS_P2ALIGNED(len, pgsz)) { 4097c478bd9Sstevel@tonic-gate (void) as_setpagesize(p->p_as, addr, len, szc, B_FALSE); 410ec25b48fSsusans p->p_stkpageszc = szc; 4117c478bd9Sstevel@tonic-gate } 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate ASSERT(err == 0); 4147c478bd9Sstevel@tonic-gate return (err); /* should always be 0 */ 4157c478bd9Sstevel@tonic-gate } 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate /* 4187c478bd9Sstevel@tonic-gate * This routine assumes that the stack grows downward. 4197c478bd9Sstevel@tonic-gate * Returns 0 on success, errno on failure. 4207c478bd9Sstevel@tonic-gate */ 4217c478bd9Sstevel@tonic-gate int 4227c478bd9Sstevel@tonic-gate grow_internal(caddr_t sp, uint_t growszc) 4237c478bd9Sstevel@tonic-gate { 4247c478bd9Sstevel@tonic-gate struct proc *p = curproc; 425ec25b48fSsusans size_t newsize; 4267c478bd9Sstevel@tonic-gate size_t oldsize; 4277c478bd9Sstevel@tonic-gate int error; 4287c478bd9Sstevel@tonic-gate size_t pgsz; 4297c478bd9Sstevel@tonic-gate uint_t szc; 4307c478bd9Sstevel@tonic-gate struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate ASSERT(sp < p->p_usrstack); 433ec25b48fSsusans sp = (caddr_t)P2ALIGN((uintptr_t)sp, PAGESIZE); 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate /* 4367c478bd9Sstevel@tonic-gate * grow to growszc alignment but use current p->p_stkpageszc for 4377c478bd9Sstevel@tonic-gate * the segvn_crargs szc passed to segvn_create. For memcntl to 4387c478bd9Sstevel@tonic-gate * increase the szc, this allows the new extension segment to be 4397c478bd9Sstevel@tonic-gate * concatenated successfully with the existing stack segment. 4407c478bd9Sstevel@tonic-gate */ 4417c478bd9Sstevel@tonic-gate if ((szc = growszc) != 0) { 4427c478bd9Sstevel@tonic-gate pgsz = page_get_pagesize(szc); 4437c478bd9Sstevel@tonic-gate ASSERT(pgsz > PAGESIZE); 444ec25b48fSsusans newsize = p->p_usrstack - (caddr_t)P2ALIGN((uintptr_t)sp, pgsz); 4457c478bd9Sstevel@tonic-gate if (newsize > (size_t)p->p_stk_ctl) { 4467c478bd9Sstevel@tonic-gate szc = 0; 4477c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 4487c478bd9Sstevel@tonic-gate newsize = p->p_usrstack - sp; 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate } else { 4517c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 452ec25b48fSsusans newsize = p->p_usrstack - sp; 4537c478bd9Sstevel@tonic-gate } 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate if (newsize > (size_t)p->p_stk_ctl) { 4567c478bd9Sstevel@tonic-gate (void) rctl_action(rctlproc_legacy[RLIMIT_STACK], p->p_rctls, p, 4577c478bd9Sstevel@tonic-gate RCA_UNSAFE_ALL); 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate return (ENOMEM); 4607c478bd9Sstevel@tonic-gate } 4617c478bd9Sstevel@tonic-gate 4627c478bd9Sstevel@tonic-gate oldsize = p->p_stksize; 4637c478bd9Sstevel@tonic-gate ASSERT(P2PHASE(oldsize, PAGESIZE) == 0); 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate if (newsize <= oldsize) { /* prevent the stack from shrinking */ 4667c478bd9Sstevel@tonic-gate return (0); 4677c478bd9Sstevel@tonic-gate } 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate if (!(p->p_stkprot & PROT_EXEC)) { 4707c478bd9Sstevel@tonic-gate crargs.prot &= ~PROT_EXEC; 4717c478bd9Sstevel@tonic-gate } 4727c478bd9Sstevel@tonic-gate /* 473ec25b48fSsusans * extend stack with the proposed new growszc, which is different 474ec25b48fSsusans * than p_stkpageszc only on a memcntl to increase the stack pagesize. 475ec25b48fSsusans * AS_MAP_NO_LPOOB means use 0, and don't reapply OOB policies via 476ec25b48fSsusans * map_pgszcvec(). Use AS_MAP_STACK to get intermediate page sizes 477ec25b48fSsusans * if not aligned to szc's pgsz. 4787c478bd9Sstevel@tonic-gate */ 479ec25b48fSsusans if (szc > 0) { 480ec25b48fSsusans caddr_t oldsp = p->p_usrstack - oldsize; 481ec25b48fSsusans caddr_t austk = (caddr_t)P2ALIGN((uintptr_t)p->p_usrstack, 482ec25b48fSsusans pgsz); 483ec25b48fSsusans 484ec25b48fSsusans if (IS_P2ALIGNED(p->p_usrstack, pgsz) || oldsp < austk) { 485ec25b48fSsusans crargs.szc = p->p_stkpageszc ? p->p_stkpageszc : 486ec25b48fSsusans AS_MAP_NO_LPOOB; 487ec25b48fSsusans } else if (oldsp == austk) { 488ec25b48fSsusans crargs.szc = szc; 489ec25b48fSsusans } else { 490ec25b48fSsusans crargs.szc = AS_MAP_STACK; 491ec25b48fSsusans } 492ec25b48fSsusans } else { 493ec25b48fSsusans crargs.szc = AS_MAP_NO_LPOOB; 494ec25b48fSsusans } 4957c478bd9Sstevel@tonic-gate crargs.lgrp_mem_policy_flags = LGRP_MP_FLAG_EXTEND_DOWN; 4967c478bd9Sstevel@tonic-gate 497ec25b48fSsusans if ((error = as_map(p->p_as, p->p_usrstack - newsize, newsize - oldsize, 4987c478bd9Sstevel@tonic-gate segvn_create, &crargs)) != 0) { 4997c478bd9Sstevel@tonic-gate if (error == EAGAIN) { 5007c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "Sorry, no swap space to grow stack " 501ae115bc7Smrj "for pid %d (%s)", p->p_pid, PTOU(p)->u_comm); 5027c478bd9Sstevel@tonic-gate } 5037c478bd9Sstevel@tonic-gate return (error); 5047c478bd9Sstevel@tonic-gate } 5057c478bd9Sstevel@tonic-gate p->p_stksize = newsize; 5067c478bd9Sstevel@tonic-gate return (0); 5077c478bd9Sstevel@tonic-gate } 5087c478bd9Sstevel@tonic-gate 50960946fe0Smec /* 510*d2a70789SRichard Lowe * Find address for user to map. If MAP_FIXED is not specified, we can pick 511*d2a70789SRichard Lowe * any address we want, but we will first try the value in *addrp if it is 512*d2a70789SRichard Lowe * non-NULL and _MAP_RANDOMIZE is not set. Thus this is implementing a way to 513*d2a70789SRichard Lowe * try and get a preferred address. 51460946fe0Smec */ 51560946fe0Smec int 51660946fe0Smec choose_addr(struct as *as, caddr_t *addrp, size_t len, offset_t off, 51760946fe0Smec int vacalign, uint_t flags) 51860946fe0Smec { 51960946fe0Smec caddr_t basep = (caddr_t)(uintptr_t)((uintptr_t)*addrp & PAGEMASK); 52060946fe0Smec size_t lenp = len; 52160946fe0Smec 52260946fe0Smec ASSERT(AS_ISCLAIMGAP(as)); /* searches should be serialized */ 52360946fe0Smec if (flags & MAP_FIXED) { 52460946fe0Smec (void) as_unmap(as, *addrp, len); 52560946fe0Smec return (0); 526*d2a70789SRichard Lowe } else if (basep != NULL && 527*d2a70789SRichard Lowe ((flags & (MAP_ALIGN | _MAP_RANDOMIZE)) == 0) && 52860946fe0Smec !as_gap(as, len, &basep, &lenp, 0, *addrp)) { 52960946fe0Smec /* User supplied address was available */ 53060946fe0Smec *addrp = basep; 53160946fe0Smec } else { 53260946fe0Smec /* 53360946fe0Smec * No user supplied address or the address supplied was not 53460946fe0Smec * available. 53560946fe0Smec */ 53660946fe0Smec map_addr(addrp, len, off, vacalign, flags); 53760946fe0Smec } 53860946fe0Smec if (*addrp == NULL) 53960946fe0Smec return (ENOMEM); 54060946fe0Smec return (0); 54160946fe0Smec } 54260946fe0Smec 54360946fe0Smec 5447c478bd9Sstevel@tonic-gate /* 5457c478bd9Sstevel@tonic-gate * Used for MAP_ANON - fast way to get anonymous pages 5467c478bd9Sstevel@tonic-gate */ 5477c478bd9Sstevel@tonic-gate static int 5487c478bd9Sstevel@tonic-gate zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags, 5497c478bd9Sstevel@tonic-gate offset_t pos) 5507c478bd9Sstevel@tonic-gate { 551ec25b48fSsusans struct segvn_crargs vn_a; 55260946fe0Smec int error; 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate if (((PROT_ALL & uprot) != uprot)) 5557c478bd9Sstevel@tonic-gate return (EACCES); 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate if ((flags & MAP_FIXED) != 0) { 5587c478bd9Sstevel@tonic-gate caddr_t userlimit; 5597c478bd9Sstevel@tonic-gate 5607c478bd9Sstevel@tonic-gate /* 5617c478bd9Sstevel@tonic-gate * Use the user address. First verify that 5627c478bd9Sstevel@tonic-gate * the address to be used is page aligned. 5637c478bd9Sstevel@tonic-gate * Then make some simple bounds checks. 5647c478bd9Sstevel@tonic-gate */ 5657c478bd9Sstevel@tonic-gate if (((uintptr_t)*addrp & PAGEOFFSET) != 0) 5667c478bd9Sstevel@tonic-gate return (EINVAL); 5677c478bd9Sstevel@tonic-gate 5687c478bd9Sstevel@tonic-gate userlimit = flags & _MAP_LOW32 ? 5697c478bd9Sstevel@tonic-gate (caddr_t)USERLIMIT32 : as->a_userlimit; 5707c478bd9Sstevel@tonic-gate switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) { 5717c478bd9Sstevel@tonic-gate case RANGE_OKAY: 5727c478bd9Sstevel@tonic-gate break; 5737c478bd9Sstevel@tonic-gate case RANGE_BADPROT: 5747c478bd9Sstevel@tonic-gate return (ENOTSUP); 5757c478bd9Sstevel@tonic-gate case RANGE_BADADDR: 5767c478bd9Sstevel@tonic-gate default: 5777c478bd9Sstevel@tonic-gate return (ENOMEM); 5787c478bd9Sstevel@tonic-gate } 57960946fe0Smec } 58060946fe0Smec /* 58160946fe0Smec * No need to worry about vac alignment for anonymous 58260946fe0Smec * pages since this is a "clone" object that doesn't 58360946fe0Smec * yet exist. 58460946fe0Smec */ 58560946fe0Smec error = choose_addr(as, addrp, len, pos, ADDR_NOVACALIGN, flags); 58660946fe0Smec if (error != 0) { 58760946fe0Smec return (error); 5887c478bd9Sstevel@tonic-gate } 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate /* 5917c478bd9Sstevel@tonic-gate * Use the seg_vn segment driver; passing in the NULL amp 5927c478bd9Sstevel@tonic-gate * gives the desired "cloning" effect. 5937c478bd9Sstevel@tonic-gate */ 594ec25b48fSsusans vn_a.vp = NULL; 595ec25b48fSsusans vn_a.offset = 0; 596ec25b48fSsusans vn_a.type = flags & MAP_TYPE; 597ec25b48fSsusans vn_a.prot = uprot; 598ec25b48fSsusans vn_a.maxprot = PROT_ALL; 599ec25b48fSsusans vn_a.flags = flags & ~MAP_TYPE; 600ec25b48fSsusans vn_a.cred = CRED(); 601ec25b48fSsusans vn_a.amp = NULL; 602ec25b48fSsusans vn_a.szc = 0; 603ec25b48fSsusans vn_a.lgrp_mem_policy_flags = 0; 604ec25b48fSsusans 605ec25b48fSsusans return (as_map(as, *addrp, len, segvn_create, &vn_a)); 6067c478bd9Sstevel@tonic-gate } 6077c478bd9Sstevel@tonic-gate 608*d2a70789SRichard Lowe #define RANDOMIZABLE_MAPPING(addr, flags) (((flags & MAP_FIXED) == 0) && \ 609*d2a70789SRichard Lowe !(((flags & MAP_ALIGN) == 0) && (addr != 0) && aslr_respect_mmap_hint)) 610*d2a70789SRichard Lowe 6117c478bd9Sstevel@tonic-gate static int 6127c478bd9Sstevel@tonic-gate smmap_common(caddr_t *addrp, size_t len, 6137c478bd9Sstevel@tonic-gate int prot, int flags, struct file *fp, offset_t pos) 6147c478bd9Sstevel@tonic-gate { 6157c478bd9Sstevel@tonic-gate struct vnode *vp; 6167c478bd9Sstevel@tonic-gate struct as *as = curproc->p_as; 6177c478bd9Sstevel@tonic-gate uint_t uprot, maxprot, type; 6187c478bd9Sstevel@tonic-gate int error; 619da6c28aaSamw int in_crit = 0; 6207c478bd9Sstevel@tonic-gate 6217c478bd9Sstevel@tonic-gate if ((flags & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | _MAP_NEW | 6227c478bd9Sstevel@tonic-gate _MAP_LOW32 | MAP_NORESERVE | MAP_ANON | MAP_ALIGN | 6237c478bd9Sstevel@tonic-gate MAP_TEXT | MAP_INITDATA)) != 0) { 6247c478bd9Sstevel@tonic-gate /* | MAP_RENAME */ /* not implemented, let user know */ 6257c478bd9Sstevel@tonic-gate return (EINVAL); 6267c478bd9Sstevel@tonic-gate } 6277c478bd9Sstevel@tonic-gate 6287c478bd9Sstevel@tonic-gate if ((flags & MAP_TEXT) && !(prot & PROT_EXEC)) { 6297c478bd9Sstevel@tonic-gate return (EINVAL); 6307c478bd9Sstevel@tonic-gate } 6317c478bd9Sstevel@tonic-gate 6327c478bd9Sstevel@tonic-gate if ((flags & (MAP_TEXT | MAP_INITDATA)) == (MAP_TEXT | MAP_INITDATA)) { 6337c478bd9Sstevel@tonic-gate return (EINVAL); 6347c478bd9Sstevel@tonic-gate } 6357c478bd9Sstevel@tonic-gate 636*d2a70789SRichard Lowe if ((flags & (MAP_FIXED | _MAP_RANDOMIZE)) == 637*d2a70789SRichard Lowe (MAP_FIXED | _MAP_RANDOMIZE)) { 638*d2a70789SRichard Lowe return (EINVAL); 639*d2a70789SRichard Lowe } 640*d2a70789SRichard Lowe 641*d2a70789SRichard Lowe /* 642*d2a70789SRichard Lowe * If it's not a fixed allocation and mmap ASLR is enabled, randomize 643*d2a70789SRichard Lowe * it. 644*d2a70789SRichard Lowe */ 645*d2a70789SRichard Lowe if (RANDOMIZABLE_MAPPING(*addrp, flags) && 646*d2a70789SRichard Lowe secflag_enabled(curproc, PROC_SEC_ASLR)) 647*d2a70789SRichard Lowe flags |= _MAP_RANDOMIZE; 648*d2a70789SRichard Lowe 6497c478bd9Sstevel@tonic-gate #if defined(__sparc) 6507c478bd9Sstevel@tonic-gate /* 6517c478bd9Sstevel@tonic-gate * See if this is an "old mmap call". If so, remember this 6527c478bd9Sstevel@tonic-gate * fact and convert the flags value given to mmap to indicate 6537c478bd9Sstevel@tonic-gate * the specified address in the system call must be used. 6547c478bd9Sstevel@tonic-gate * _MAP_NEW is turned set by all new uses of mmap. 6557c478bd9Sstevel@tonic-gate */ 6567c478bd9Sstevel@tonic-gate if ((flags & _MAP_NEW) == 0) 6577c478bd9Sstevel@tonic-gate flags |= MAP_FIXED; 6587c478bd9Sstevel@tonic-gate #endif 6597c478bd9Sstevel@tonic-gate flags &= ~_MAP_NEW; 6607c478bd9Sstevel@tonic-gate 6617c478bd9Sstevel@tonic-gate type = flags & MAP_TYPE; 6627c478bd9Sstevel@tonic-gate if (type != MAP_PRIVATE && type != MAP_SHARED) 6637c478bd9Sstevel@tonic-gate return (EINVAL); 6647c478bd9Sstevel@tonic-gate 6657c478bd9Sstevel@tonic-gate 6667c478bd9Sstevel@tonic-gate if (flags & MAP_ALIGN) { 6677c478bd9Sstevel@tonic-gate if (flags & MAP_FIXED) 6687c478bd9Sstevel@tonic-gate return (EINVAL); 6697c478bd9Sstevel@tonic-gate 6707c478bd9Sstevel@tonic-gate /* alignment needs to be a power of 2 >= page size */ 6717c478bd9Sstevel@tonic-gate if (((uintptr_t)*addrp < PAGESIZE && (uintptr_t)*addrp != 0) || 67260946fe0Smec !ISP2((uintptr_t)*addrp)) 6737c478bd9Sstevel@tonic-gate return (EINVAL); 6747c478bd9Sstevel@tonic-gate } 6757c478bd9Sstevel@tonic-gate /* 6767c478bd9Sstevel@tonic-gate * Check for bad lengths and file position. 6777c478bd9Sstevel@tonic-gate * We let the VOP_MAP routine check for negative lengths 6787c478bd9Sstevel@tonic-gate * since on some vnode types this might be appropriate. 6797c478bd9Sstevel@tonic-gate */ 6807c478bd9Sstevel@tonic-gate if (len == 0 || (pos & (u_offset_t)PAGEOFFSET) != 0) 6817c478bd9Sstevel@tonic-gate return (EINVAL); 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate maxprot = PROT_ALL; /* start out allowing all accesses */ 6847c478bd9Sstevel@tonic-gate uprot = prot | PROT_USER; 6857c478bd9Sstevel@tonic-gate 6867c478bd9Sstevel@tonic-gate if (fp == NULL) { 6877c478bd9Sstevel@tonic-gate ASSERT(flags & MAP_ANON); 688783f4f5eSRoger A. Faulkner /* discard lwpchan mappings, like munmap() */ 689783f4f5eSRoger A. Faulkner if ((flags & MAP_FIXED) && curproc->p_lcp != NULL) 690783f4f5eSRoger A. Faulkner lwpchan_delete_mapping(curproc, *addrp, *addrp + len); 6917c478bd9Sstevel@tonic-gate as_rangelock(as); 6927c478bd9Sstevel@tonic-gate error = zmap(as, addrp, len, uprot, flags, pos); 6937c478bd9Sstevel@tonic-gate as_rangeunlock(as); 6942c5124a1SPrashanth Sreenivasa /* 6952c5124a1SPrashanth Sreenivasa * Tell machine specific code that lwp has mapped shared memory 6962c5124a1SPrashanth Sreenivasa */ 6972c5124a1SPrashanth Sreenivasa if (error == 0 && (flags & MAP_SHARED)) { 6982c5124a1SPrashanth Sreenivasa /* EMPTY */ 6992c5124a1SPrashanth Sreenivasa LWP_MMODEL_SHARED_AS(*addrp, len); 7002c5124a1SPrashanth Sreenivasa } 7017c478bd9Sstevel@tonic-gate return (error); 7027c478bd9Sstevel@tonic-gate } else if ((flags & MAP_ANON) != 0) 7037c478bd9Sstevel@tonic-gate return (EINVAL); 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate vp = fp->f_vnode; 7067c478bd9Sstevel@tonic-gate 7077c478bd9Sstevel@tonic-gate /* Can't execute code from "noexec" mounted filesystem. */ 7087c478bd9Sstevel@tonic-gate if ((vp->v_vfsp->vfs_flag & VFS_NOEXEC) != 0) 7097c478bd9Sstevel@tonic-gate maxprot &= ~PROT_EXEC; 7107c478bd9Sstevel@tonic-gate 7117c478bd9Sstevel@tonic-gate /* 7127c478bd9Sstevel@tonic-gate * These checks were added as part of large files. 7137c478bd9Sstevel@tonic-gate * 71449a63d68Speterte * Return ENXIO if the initial position is negative; return EOVERFLOW 7157c478bd9Sstevel@tonic-gate * if (offset + len) would overflow the maximum allowed offset for the 7167c478bd9Sstevel@tonic-gate * type of file descriptor being used. 7177c478bd9Sstevel@tonic-gate */ 7187c478bd9Sstevel@tonic-gate if (vp->v_type == VREG) { 71949a63d68Speterte if (pos < 0) 72049a63d68Speterte return (ENXIO); 7217c478bd9Sstevel@tonic-gate if ((offset_t)len > (OFFSET_MAX(fp) - pos)) 7227c478bd9Sstevel@tonic-gate return (EOVERFLOW); 7237c478bd9Sstevel@tonic-gate } 7247c478bd9Sstevel@tonic-gate 7257c478bd9Sstevel@tonic-gate if (type == MAP_SHARED && (fp->f_flag & FWRITE) == 0) { 7267c478bd9Sstevel@tonic-gate /* no write access allowed */ 7277c478bd9Sstevel@tonic-gate maxprot &= ~PROT_WRITE; 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate 7307c478bd9Sstevel@tonic-gate /* 7317c478bd9Sstevel@tonic-gate * XXX - Do we also adjust maxprot based on protections 7327c478bd9Sstevel@tonic-gate * of the vnode? E.g. if no execute permission is given 7337c478bd9Sstevel@tonic-gate * on the vnode for the current user, maxprot probably 7347c478bd9Sstevel@tonic-gate * should disallow PROT_EXEC also? This is different 7357c478bd9Sstevel@tonic-gate * from the write access as this would be a per vnode 7367c478bd9Sstevel@tonic-gate * test as opposed to a per fd test for writability. 7377c478bd9Sstevel@tonic-gate */ 7387c478bd9Sstevel@tonic-gate 7397c478bd9Sstevel@tonic-gate /* 7407c478bd9Sstevel@tonic-gate * Verify that the specified protections are not greater than 7417c478bd9Sstevel@tonic-gate * the maximum allowable protections. Also test to make sure 7427c478bd9Sstevel@tonic-gate * that the file descriptor does allows for read access since 7437c478bd9Sstevel@tonic-gate * "write only" mappings are hard to do since normally we do 7447c478bd9Sstevel@tonic-gate * the read from the file before the page can be written. 7457c478bd9Sstevel@tonic-gate */ 7467c478bd9Sstevel@tonic-gate if (((maxprot & uprot) != uprot) || (fp->f_flag & FREAD) == 0) 7477c478bd9Sstevel@tonic-gate return (EACCES); 7487c478bd9Sstevel@tonic-gate 7497c478bd9Sstevel@tonic-gate /* 7507c478bd9Sstevel@tonic-gate * If the user specified an address, do some simple checks here 7517c478bd9Sstevel@tonic-gate */ 7527c478bd9Sstevel@tonic-gate if ((flags & MAP_FIXED) != 0) { 7537c478bd9Sstevel@tonic-gate caddr_t userlimit; 7547c478bd9Sstevel@tonic-gate 7557c478bd9Sstevel@tonic-gate /* 7567c478bd9Sstevel@tonic-gate * Use the user address. First verify that 7577c478bd9Sstevel@tonic-gate * the address to be used is page aligned. 7587c478bd9Sstevel@tonic-gate * Then make some simple bounds checks. 7597c478bd9Sstevel@tonic-gate */ 7607c478bd9Sstevel@tonic-gate if (((uintptr_t)*addrp & PAGEOFFSET) != 0) 7617c478bd9Sstevel@tonic-gate return (EINVAL); 7627c478bd9Sstevel@tonic-gate 7637c478bd9Sstevel@tonic-gate userlimit = flags & _MAP_LOW32 ? 7647c478bd9Sstevel@tonic-gate (caddr_t)USERLIMIT32 : as->a_userlimit; 7657c478bd9Sstevel@tonic-gate switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) { 7667c478bd9Sstevel@tonic-gate case RANGE_OKAY: 7677c478bd9Sstevel@tonic-gate break; 7687c478bd9Sstevel@tonic-gate case RANGE_BADPROT: 7697c478bd9Sstevel@tonic-gate return (ENOTSUP); 7707c478bd9Sstevel@tonic-gate case RANGE_BADADDR: 7717c478bd9Sstevel@tonic-gate default: 7727c478bd9Sstevel@tonic-gate return (ENOMEM); 7737c478bd9Sstevel@tonic-gate } 7747c478bd9Sstevel@tonic-gate } 7757c478bd9Sstevel@tonic-gate 776da6c28aaSamw if ((prot & (PROT_READ | PROT_WRITE | PROT_EXEC)) && 777da6c28aaSamw nbl_need_check(vp)) { 778da6c28aaSamw int svmand; 779da6c28aaSamw nbl_op_t nop; 780da6c28aaSamw 781da6c28aaSamw nbl_start_crit(vp, RW_READER); 782da6c28aaSamw in_crit = 1; 783da6c28aaSamw error = nbl_svmand(vp, fp->f_cred, &svmand); 784da6c28aaSamw if (error != 0) 785da6c28aaSamw goto done; 786da6c28aaSamw if ((prot & PROT_WRITE) && (type == MAP_SHARED)) { 787da6c28aaSamw if (prot & (PROT_READ | PROT_EXEC)) { 788da6c28aaSamw nop = NBL_READWRITE; 789da6c28aaSamw } else { 790da6c28aaSamw nop = NBL_WRITE; 791da6c28aaSamw } 792da6c28aaSamw } else { 793da6c28aaSamw nop = NBL_READ; 794da6c28aaSamw } 795da6c28aaSamw if (nbl_conflict(vp, nop, 0, LONG_MAX, svmand, NULL)) { 796da6c28aaSamw error = EACCES; 797da6c28aaSamw goto done; 798da6c28aaSamw } 799da6c28aaSamw } 8007c478bd9Sstevel@tonic-gate 801783f4f5eSRoger A. Faulkner /* discard lwpchan mappings, like munmap() */ 802783f4f5eSRoger A. Faulkner if ((flags & MAP_FIXED) && curproc->p_lcp != NULL) 803783f4f5eSRoger A. Faulkner lwpchan_delete_mapping(curproc, *addrp, *addrp + len); 804783f4f5eSRoger A. Faulkner 8057c478bd9Sstevel@tonic-gate /* 8067c478bd9Sstevel@tonic-gate * Ok, now let the vnode map routine do its thing to set things up. 8077c478bd9Sstevel@tonic-gate */ 8087c478bd9Sstevel@tonic-gate error = VOP_MAP(vp, pos, as, 809da6c28aaSamw addrp, len, uprot, maxprot, flags, fp->f_cred, NULL); 8107c478bd9Sstevel@tonic-gate 8117c478bd9Sstevel@tonic-gate if (error == 0) { 8122c5124a1SPrashanth Sreenivasa /* 8132c5124a1SPrashanth Sreenivasa * Tell machine specific code that lwp has mapped shared memory 8142c5124a1SPrashanth Sreenivasa */ 8152c5124a1SPrashanth Sreenivasa if (flags & MAP_SHARED) { 8162c5124a1SPrashanth Sreenivasa /* EMPTY */ 8172c5124a1SPrashanth Sreenivasa LWP_MMODEL_SHARED_AS(*addrp, len); 8182c5124a1SPrashanth Sreenivasa } 8197c478bd9Sstevel@tonic-gate if (vp->v_type == VREG && 8207c478bd9Sstevel@tonic-gate (flags & (MAP_TEXT | MAP_INITDATA)) != 0) { 8217c478bd9Sstevel@tonic-gate /* 8227c478bd9Sstevel@tonic-gate * Mark this as an executable vnode 8237c478bd9Sstevel@tonic-gate */ 8247c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 8257c478bd9Sstevel@tonic-gate vp->v_flag |= VVMEXEC; 8267c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 8277c478bd9Sstevel@tonic-gate } 8287c478bd9Sstevel@tonic-gate } 8297c478bd9Sstevel@tonic-gate 830da6c28aaSamw done: 831da6c28aaSamw if (in_crit) 832da6c28aaSamw nbl_end_crit(vp); 8337c478bd9Sstevel@tonic-gate return (error); 8347c478bd9Sstevel@tonic-gate } 8357c478bd9Sstevel@tonic-gate 8367c478bd9Sstevel@tonic-gate #ifdef _LP64 8377c478bd9Sstevel@tonic-gate /* 8387c478bd9Sstevel@tonic-gate * LP64 mmap(2) system call: 64-bit offset, 64-bit address. 8397c478bd9Sstevel@tonic-gate * 8407c478bd9Sstevel@tonic-gate * The "large file" mmap routine mmap64(2) is also mapped to this routine 8417c478bd9Sstevel@tonic-gate * by the 64-bit version of libc. 8427c478bd9Sstevel@tonic-gate * 8437c478bd9Sstevel@tonic-gate * Eventually, this should be the only version, and have smmap_common() 8447c478bd9Sstevel@tonic-gate * folded back into it again. Some day. 8457c478bd9Sstevel@tonic-gate */ 8467c478bd9Sstevel@tonic-gate caddr_t 8477c478bd9Sstevel@tonic-gate smmap64(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos) 8487c478bd9Sstevel@tonic-gate { 8497c478bd9Sstevel@tonic-gate struct file *fp; 8507c478bd9Sstevel@tonic-gate int error; 8517c478bd9Sstevel@tonic-gate 8521b3b16f3STheo Schlossnagle if (fd == -1 && (flags & MAP_ANON) != 0) 8537c478bd9Sstevel@tonic-gate error = smmap_common(&addr, len, prot, flags, 8547c478bd9Sstevel@tonic-gate NULL, (offset_t)pos); 8557c478bd9Sstevel@tonic-gate else if ((fp = getf(fd)) != NULL) { 8567c478bd9Sstevel@tonic-gate error = smmap_common(&addr, len, prot, flags, 8577c478bd9Sstevel@tonic-gate fp, (offset_t)pos); 8587c478bd9Sstevel@tonic-gate releasef(fd); 8597c478bd9Sstevel@tonic-gate } else 8607c478bd9Sstevel@tonic-gate error = EBADF; 8617c478bd9Sstevel@tonic-gate 8627c478bd9Sstevel@tonic-gate return (error ? (caddr_t)(uintptr_t)set_errno(error) : addr); 8637c478bd9Sstevel@tonic-gate } 8647c478bd9Sstevel@tonic-gate #endif /* _LP64 */ 8657c478bd9Sstevel@tonic-gate 8667c478bd9Sstevel@tonic-gate #if defined(_SYSCALL32_IMPL) || defined(_ILP32) 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate /* 8697c478bd9Sstevel@tonic-gate * ILP32 mmap(2) system call: 32-bit offset, 32-bit address. 8707c478bd9Sstevel@tonic-gate */ 8717c478bd9Sstevel@tonic-gate caddr_t 8727c478bd9Sstevel@tonic-gate smmap32(caddr32_t addr, size32_t len, int prot, int flags, int fd, off32_t pos) 8737c478bd9Sstevel@tonic-gate { 8747c478bd9Sstevel@tonic-gate struct file *fp; 8757c478bd9Sstevel@tonic-gate int error; 8767c478bd9Sstevel@tonic-gate caddr_t a = (caddr_t)(uintptr_t)addr; 8777c478bd9Sstevel@tonic-gate 8787c478bd9Sstevel@tonic-gate if (flags & _MAP_LOW32) 8797c478bd9Sstevel@tonic-gate error = EINVAL; 8807c478bd9Sstevel@tonic-gate else if (fd == -1 && (flags & MAP_ANON) != 0) 8817c478bd9Sstevel@tonic-gate error = smmap_common(&a, (size_t)len, prot, 8827c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, NULL, (offset_t)pos); 8837c478bd9Sstevel@tonic-gate else if ((fp = getf(fd)) != NULL) { 8847c478bd9Sstevel@tonic-gate error = smmap_common(&a, (size_t)len, prot, 8857c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, fp, (offset_t)pos); 8867c478bd9Sstevel@tonic-gate releasef(fd); 8877c478bd9Sstevel@tonic-gate } else 8887c478bd9Sstevel@tonic-gate error = EBADF; 8897c478bd9Sstevel@tonic-gate 8907c478bd9Sstevel@tonic-gate ASSERT(error != 0 || (uintptr_t)(a + len) < (uintptr_t)UINT32_MAX); 8917c478bd9Sstevel@tonic-gate 8927c478bd9Sstevel@tonic-gate return (error ? (caddr_t)(uintptr_t)set_errno(error) : a); 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate 8957c478bd9Sstevel@tonic-gate /* 8967c478bd9Sstevel@tonic-gate * ILP32 mmap64(2) system call: 64-bit offset, 32-bit address. 8977c478bd9Sstevel@tonic-gate * 8987c478bd9Sstevel@tonic-gate * Now things really get ugly because we can't use the C-style 8997c478bd9Sstevel@tonic-gate * calling convention for more than 6 args, and 64-bit parameter 9007c478bd9Sstevel@tonic-gate * passing on 32-bit systems is less than clean. 9017c478bd9Sstevel@tonic-gate */ 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate struct mmaplf32a { 9047c478bd9Sstevel@tonic-gate caddr_t addr; 9057c478bd9Sstevel@tonic-gate size_t len; 9067c478bd9Sstevel@tonic-gate #ifdef _LP64 9077c478bd9Sstevel@tonic-gate /* 9087c478bd9Sstevel@tonic-gate * 32-bit contents, 64-bit cells 9097c478bd9Sstevel@tonic-gate */ 9107c478bd9Sstevel@tonic-gate uint64_t prot; 9117c478bd9Sstevel@tonic-gate uint64_t flags; 9127c478bd9Sstevel@tonic-gate uint64_t fd; 9137c478bd9Sstevel@tonic-gate uint64_t offhi; 9147c478bd9Sstevel@tonic-gate uint64_t offlo; 9157c478bd9Sstevel@tonic-gate #else 9167c478bd9Sstevel@tonic-gate /* 9177c478bd9Sstevel@tonic-gate * 32-bit contents, 32-bit cells 9187c478bd9Sstevel@tonic-gate */ 9197c478bd9Sstevel@tonic-gate uint32_t prot; 9207c478bd9Sstevel@tonic-gate uint32_t flags; 9217c478bd9Sstevel@tonic-gate uint32_t fd; 9227c478bd9Sstevel@tonic-gate uint32_t offhi; 9237c478bd9Sstevel@tonic-gate uint32_t offlo; 9247c478bd9Sstevel@tonic-gate #endif 9257c478bd9Sstevel@tonic-gate }; 9267c478bd9Sstevel@tonic-gate 9277c478bd9Sstevel@tonic-gate int 9287c478bd9Sstevel@tonic-gate smmaplf32(struct mmaplf32a *uap, rval_t *rvp) 9297c478bd9Sstevel@tonic-gate { 9307c478bd9Sstevel@tonic-gate struct file *fp; 9317c478bd9Sstevel@tonic-gate int error; 9327c478bd9Sstevel@tonic-gate caddr_t a = uap->addr; 9337c478bd9Sstevel@tonic-gate int flags = (int)uap->flags; 9347c478bd9Sstevel@tonic-gate int fd = (int)uap->fd; 9357c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 9367c478bd9Sstevel@tonic-gate offset_t off = ((u_offset_t)uap->offhi << 32) | (u_offset_t)uap->offlo; 9377c478bd9Sstevel@tonic-gate #else 9387c478bd9Sstevel@tonic-gate offset_t off = ((u_offset_t)uap->offlo << 32) | (u_offset_t)uap->offhi; 9397c478bd9Sstevel@tonic-gate #endif 9407c478bd9Sstevel@tonic-gate 9417c478bd9Sstevel@tonic-gate if (flags & _MAP_LOW32) 9427c478bd9Sstevel@tonic-gate error = EINVAL; 9437c478bd9Sstevel@tonic-gate else if (fd == -1 && (flags & MAP_ANON) != 0) 9447c478bd9Sstevel@tonic-gate error = smmap_common(&a, uap->len, (int)uap->prot, 9457c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, NULL, off); 9467c478bd9Sstevel@tonic-gate else if ((fp = getf(fd)) != NULL) { 9477c478bd9Sstevel@tonic-gate error = smmap_common(&a, uap->len, (int)uap->prot, 9487c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, fp, off); 9497c478bd9Sstevel@tonic-gate releasef(fd); 9507c478bd9Sstevel@tonic-gate } else 9517c478bd9Sstevel@tonic-gate error = EBADF; 9527c478bd9Sstevel@tonic-gate 9537c478bd9Sstevel@tonic-gate if (error == 0) 9547c478bd9Sstevel@tonic-gate rvp->r_val1 = (uintptr_t)a; 9557c478bd9Sstevel@tonic-gate return (error); 9567c478bd9Sstevel@tonic-gate } 9577c478bd9Sstevel@tonic-gate 9587c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL || _ILP32 */ 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate int 9617c478bd9Sstevel@tonic-gate munmap(caddr_t addr, size_t len) 9627c478bd9Sstevel@tonic-gate { 9637c478bd9Sstevel@tonic-gate struct proc *p = curproc; 9647c478bd9Sstevel@tonic-gate struct as *as = p->p_as; 9657c478bd9Sstevel@tonic-gate 9667c478bd9Sstevel@tonic-gate if (((uintptr_t)addr & PAGEOFFSET) != 0 || len == 0) 9677c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 9687c478bd9Sstevel@tonic-gate 9697c478bd9Sstevel@tonic-gate if (valid_usr_range(addr, len, 0, as, as->a_userlimit) != RANGE_OKAY) 9707c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 9717c478bd9Sstevel@tonic-gate 9727c478bd9Sstevel@tonic-gate /* 9737c478bd9Sstevel@tonic-gate * Discard lwpchan mappings. 9747c478bd9Sstevel@tonic-gate */ 9757c478bd9Sstevel@tonic-gate if (p->p_lcp != NULL) 9767c478bd9Sstevel@tonic-gate lwpchan_delete_mapping(p, addr, addr + len); 9777c478bd9Sstevel@tonic-gate if (as_unmap(as, addr, len) != 0) 9787c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 9797c478bd9Sstevel@tonic-gate 9807c478bd9Sstevel@tonic-gate return (0); 9817c478bd9Sstevel@tonic-gate } 9827c478bd9Sstevel@tonic-gate 9837c478bd9Sstevel@tonic-gate int 9847c478bd9Sstevel@tonic-gate mprotect(caddr_t addr, size_t len, int prot) 9857c478bd9Sstevel@tonic-gate { 9867c478bd9Sstevel@tonic-gate struct as *as = curproc->p_as; 9877c478bd9Sstevel@tonic-gate uint_t uprot = prot | PROT_USER; 9887c478bd9Sstevel@tonic-gate int error; 9897c478bd9Sstevel@tonic-gate 9907c478bd9Sstevel@tonic-gate if (((uintptr_t)addr & PAGEOFFSET) != 0 || len == 0) 9917c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 9927c478bd9Sstevel@tonic-gate 9937c478bd9Sstevel@tonic-gate switch (valid_usr_range(addr, len, prot, as, as->a_userlimit)) { 9947c478bd9Sstevel@tonic-gate case RANGE_OKAY: 9957c478bd9Sstevel@tonic-gate break; 9967c478bd9Sstevel@tonic-gate case RANGE_BADPROT: 9977c478bd9Sstevel@tonic-gate return (set_errno(ENOTSUP)); 9987c478bd9Sstevel@tonic-gate case RANGE_BADADDR: 9997c478bd9Sstevel@tonic-gate default: 10007c478bd9Sstevel@tonic-gate return (set_errno(ENOMEM)); 10017c478bd9Sstevel@tonic-gate } 10027c478bd9Sstevel@tonic-gate 10037c478bd9Sstevel@tonic-gate error = as_setprot(as, addr, len, uprot); 10047c478bd9Sstevel@tonic-gate if (error) 10057c478bd9Sstevel@tonic-gate return (set_errno(error)); 10067c478bd9Sstevel@tonic-gate return (0); 10077c478bd9Sstevel@tonic-gate } 10087c478bd9Sstevel@tonic-gate 10097c478bd9Sstevel@tonic-gate #define MC_CACHE 128 /* internal result buffer */ 10107c478bd9Sstevel@tonic-gate #define MC_QUANTUM (MC_CACHE * PAGESIZE) /* addresses covered in loop */ 10117c478bd9Sstevel@tonic-gate 10127c478bd9Sstevel@tonic-gate int 10137c478bd9Sstevel@tonic-gate mincore(caddr_t addr, size_t len, char *vecp) 10147c478bd9Sstevel@tonic-gate { 10157c478bd9Sstevel@tonic-gate struct as *as = curproc->p_as; 10167c478bd9Sstevel@tonic-gate caddr_t ea; /* end address of loop */ 10177c478bd9Sstevel@tonic-gate size_t rl; /* inner result length */ 10187c478bd9Sstevel@tonic-gate char vec[MC_CACHE]; /* local vector cache */ 10197c478bd9Sstevel@tonic-gate int error; 10207c478bd9Sstevel@tonic-gate model_t model; 10217c478bd9Sstevel@tonic-gate long llen; 10227c478bd9Sstevel@tonic-gate 10237c478bd9Sstevel@tonic-gate model = get_udatamodel(); 10247c478bd9Sstevel@tonic-gate /* 10257c478bd9Sstevel@tonic-gate * Validate form of address parameters. 10267c478bd9Sstevel@tonic-gate */ 10277c478bd9Sstevel@tonic-gate if (model == DATAMODEL_NATIVE) { 10287c478bd9Sstevel@tonic-gate llen = (long)len; 10297c478bd9Sstevel@tonic-gate } else { 10307c478bd9Sstevel@tonic-gate llen = (int32_t)(size32_t)len; 10317c478bd9Sstevel@tonic-gate } 10327c478bd9Sstevel@tonic-gate if (((uintptr_t)addr & PAGEOFFSET) != 0 || llen <= 0) 10337c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 10347c478bd9Sstevel@tonic-gate 10357c478bd9Sstevel@tonic-gate if (valid_usr_range(addr, len, 0, as, as->a_userlimit) != RANGE_OKAY) 10367c478bd9Sstevel@tonic-gate return (set_errno(ENOMEM)); 10377c478bd9Sstevel@tonic-gate 10387c478bd9Sstevel@tonic-gate /* 10397c478bd9Sstevel@tonic-gate * Loop over subranges of interval [addr : addr + len), recovering 10407c478bd9Sstevel@tonic-gate * results internally and then copying them out to caller. Subrange 10417c478bd9Sstevel@tonic-gate * is based on the size of MC_CACHE, defined above. 10427c478bd9Sstevel@tonic-gate */ 10437c478bd9Sstevel@tonic-gate for (ea = addr + len; addr < ea; addr += MC_QUANTUM) { 10447c478bd9Sstevel@tonic-gate error = as_incore(as, addr, 10457c478bd9Sstevel@tonic-gate (size_t)MIN(MC_QUANTUM, ea - addr), vec, &rl); 10467c478bd9Sstevel@tonic-gate if (rl != 0) { 10477c478bd9Sstevel@tonic-gate rl = (rl + PAGESIZE - 1) / PAGESIZE; 10487c478bd9Sstevel@tonic-gate if (copyout(vec, vecp, rl) != 0) 10497c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 10507c478bd9Sstevel@tonic-gate vecp += rl; 10517c478bd9Sstevel@tonic-gate } 10527c478bd9Sstevel@tonic-gate if (error != 0) 10537c478bd9Sstevel@tonic-gate return (set_errno(ENOMEM)); 10547c478bd9Sstevel@tonic-gate } 10557c478bd9Sstevel@tonic-gate return (0); 10567c478bd9Sstevel@tonic-gate } 1057