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 */ 217c478bd9Sstevel@tonic-gate /* 22*ae115bc7Smrj * Copyright 2007 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 647c478bd9Sstevel@tonic-gate static int brk_lpg(caddr_t nva); 657c478bd9Sstevel@tonic-gate static int grow_lpg(caddr_t sp); 667c478bd9Sstevel@tonic-gate 677c478bd9Sstevel@tonic-gate int 687c478bd9Sstevel@tonic-gate brk(caddr_t nva) 697c478bd9Sstevel@tonic-gate { 707c478bd9Sstevel@tonic-gate int error; 717c478bd9Sstevel@tonic-gate proc_t *p = curproc; 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate /* 747c478bd9Sstevel@tonic-gate * Serialize brk operations on an address space. 757c478bd9Sstevel@tonic-gate * This also serves as the lock protecting p_brksize 767c478bd9Sstevel@tonic-gate * and p_brkpageszc. 777c478bd9Sstevel@tonic-gate */ 787c478bd9Sstevel@tonic-gate as_rangelock(p->p_as); 797c478bd9Sstevel@tonic-gate if (use_brk_lpg && (p->p_flag & SAUTOLPG) != 0) { 807c478bd9Sstevel@tonic-gate error = brk_lpg(nva); 817c478bd9Sstevel@tonic-gate } else { 827c478bd9Sstevel@tonic-gate error = brk_internal(nva, p->p_brkpageszc); 837c478bd9Sstevel@tonic-gate } 847c478bd9Sstevel@tonic-gate as_rangeunlock(p->p_as); 857c478bd9Sstevel@tonic-gate return ((error != 0 ? set_errno(error) : 0)); 867c478bd9Sstevel@tonic-gate } 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate /* 897c478bd9Sstevel@tonic-gate * Algorithm: call arch-specific map_pgsz to get best page size to use, 907c478bd9Sstevel@tonic-gate * then call brk_internal(). 917c478bd9Sstevel@tonic-gate * Returns 0 on success. 927c478bd9Sstevel@tonic-gate */ 937c478bd9Sstevel@tonic-gate static int 947c478bd9Sstevel@tonic-gate brk_lpg(caddr_t nva) 957c478bd9Sstevel@tonic-gate { 967c478bd9Sstevel@tonic-gate struct proc *p = curproc; 977c478bd9Sstevel@tonic-gate size_t pgsz, len; 98ec25b48fSsusans caddr_t addr, brkend; 997c478bd9Sstevel@tonic-gate caddr_t bssbase = p->p_bssbase; 1007c478bd9Sstevel@tonic-gate caddr_t brkbase = p->p_brkbase; 1017c478bd9Sstevel@tonic-gate int oszc, szc; 1027c478bd9Sstevel@tonic-gate int err; 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate oszc = p->p_brkpageszc; 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate /* 1077c478bd9Sstevel@tonic-gate * If p_brkbase has not yet been set, the first call 1087c478bd9Sstevel@tonic-gate * to brk_internal() will initialize it. 1097c478bd9Sstevel@tonic-gate */ 1107c478bd9Sstevel@tonic-gate if (brkbase == 0) { 1117c478bd9Sstevel@tonic-gate return (brk_internal(nva, oszc)); 1127c478bd9Sstevel@tonic-gate } 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate len = nva - bssbase; 1157c478bd9Sstevel@tonic-gate 116ec25b48fSsusans pgsz = map_pgsz(MAPPGSZ_HEAP, p, bssbase, len, 0); 1177c478bd9Sstevel@tonic-gate szc = page_szc(pgsz); 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate /* 1207c478bd9Sstevel@tonic-gate * Covers two cases: 1217c478bd9Sstevel@tonic-gate * 1. page_szc() returns -1 for invalid page size, so we want to 1227c478bd9Sstevel@tonic-gate * ignore it in that case. 1237c478bd9Sstevel@tonic-gate * 2. By design we never decrease page size, as it is more stable. 1247c478bd9Sstevel@tonic-gate */ 1257c478bd9Sstevel@tonic-gate if (szc <= oszc) { 1267c478bd9Sstevel@tonic-gate err = brk_internal(nva, oszc); 1277c478bd9Sstevel@tonic-gate /* If failed, back off to base page size. */ 1287c478bd9Sstevel@tonic-gate if (err != 0 && oszc != 0) { 1297c478bd9Sstevel@tonic-gate err = brk_internal(nva, 0); 1307c478bd9Sstevel@tonic-gate } 1317c478bd9Sstevel@tonic-gate return (err); 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate 1347c478bd9Sstevel@tonic-gate err = brk_internal(nva, szc); 1357c478bd9Sstevel@tonic-gate /* If using szc failed, map with base page size and return. */ 1367c478bd9Sstevel@tonic-gate if (err != 0) { 1377c478bd9Sstevel@tonic-gate if (szc != 0) { 1387c478bd9Sstevel@tonic-gate err = brk_internal(nva, 0); 1397c478bd9Sstevel@tonic-gate } 1407c478bd9Sstevel@tonic-gate return (err); 1417c478bd9Sstevel@tonic-gate } 1427c478bd9Sstevel@tonic-gate 143ec25b48fSsusans /* 144ec25b48fSsusans * Round up brk base to a large page boundary and remap 145ec25b48fSsusans * anything in the segment already faulted in beyond that 146ec25b48fSsusans * point. 147ec25b48fSsusans */ 148ec25b48fSsusans addr = (caddr_t)P2ROUNDUP((uintptr_t)p->p_bssbase, pgsz); 149ec25b48fSsusans brkend = brkbase + p->p_brksize; 150ec25b48fSsusans len = brkend - addr; 151ec25b48fSsusans /* Check that len is not negative. Update page size code for heap. */ 152ec25b48fSsusans if (addr >= p->p_bssbase && brkend > addr && IS_P2ALIGNED(len, pgsz)) { 1537c478bd9Sstevel@tonic-gate (void) as_setpagesize(p->p_as, addr, len, szc, B_FALSE); 154ec25b48fSsusans p->p_brkpageszc = szc; 1557c478bd9Sstevel@tonic-gate } 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate ASSERT(err == 0); 1587c478bd9Sstevel@tonic-gate return (err); /* should always be 0 */ 1597c478bd9Sstevel@tonic-gate } 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate /* 1627c478bd9Sstevel@tonic-gate * Returns 0 on success. 1637c478bd9Sstevel@tonic-gate */ 1647c478bd9Sstevel@tonic-gate int 1657c478bd9Sstevel@tonic-gate brk_internal(caddr_t nva, uint_t brkszc) 1667c478bd9Sstevel@tonic-gate { 1677c478bd9Sstevel@tonic-gate caddr_t ova; /* current break address */ 1687c478bd9Sstevel@tonic-gate size_t size; 1697c478bd9Sstevel@tonic-gate int error; 1707c478bd9Sstevel@tonic-gate struct proc *p = curproc; 1717c478bd9Sstevel@tonic-gate struct as *as = p->p_as; 1727c478bd9Sstevel@tonic-gate size_t pgsz; 1737c478bd9Sstevel@tonic-gate uint_t szc; 1747c478bd9Sstevel@tonic-gate rctl_qty_t as_rctl; 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate /* 1777c478bd9Sstevel@tonic-gate * extend heap to brkszc alignment but use current p->p_brkpageszc 1787c478bd9Sstevel@tonic-gate * for the newly created segment. This allows the new extension 1797c478bd9Sstevel@tonic-gate * segment to be concatenated successfully with the existing brk 1807c478bd9Sstevel@tonic-gate * segment. 1817c478bd9Sstevel@tonic-gate */ 1827c478bd9Sstevel@tonic-gate if ((szc = brkszc) != 0) { 1837c478bd9Sstevel@tonic-gate pgsz = page_get_pagesize(szc); 1847c478bd9Sstevel@tonic-gate ASSERT(pgsz > PAGESIZE); 1857c478bd9Sstevel@tonic-gate } else { 1867c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 1877c478bd9Sstevel@tonic-gate } 1887c478bd9Sstevel@tonic-gate 1897c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 1907c478bd9Sstevel@tonic-gate as_rctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_DATA], 1917c478bd9Sstevel@tonic-gate p->p_rctls, p); 1927c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate /* 1957c478bd9Sstevel@tonic-gate * If p_brkbase has not yet been set, the first call 1967c478bd9Sstevel@tonic-gate * to brk() will initialize it. 1977c478bd9Sstevel@tonic-gate */ 1987c478bd9Sstevel@tonic-gate if (p->p_brkbase == 0) 1997c478bd9Sstevel@tonic-gate p->p_brkbase = nva; 2007c478bd9Sstevel@tonic-gate 2017c478bd9Sstevel@tonic-gate /* 2027c478bd9Sstevel@tonic-gate * Before multiple page size support existed p_brksize was the value 2037c478bd9Sstevel@tonic-gate * not rounded to the pagesize (i.e. it stored the exact user request 2047c478bd9Sstevel@tonic-gate * for heap size). If pgsz is greater than PAGESIZE calculate the 2057c478bd9Sstevel@tonic-gate * heap size as the real new heap size by rounding it up to pgsz. 2067c478bd9Sstevel@tonic-gate * This is useful since we may want to know where the heap ends 2077c478bd9Sstevel@tonic-gate * without knowing heap pagesize (e.g. some old code) and also if 2087c478bd9Sstevel@tonic-gate * heap pagesize changes we can update p_brkpageszc but delay adding 2097c478bd9Sstevel@tonic-gate * new mapping yet still know from p_brksize where the heap really 2107c478bd9Sstevel@tonic-gate * ends. The user requested heap end is stored in libc variable. 2117c478bd9Sstevel@tonic-gate */ 2127c478bd9Sstevel@tonic-gate if (pgsz > PAGESIZE) { 2137c478bd9Sstevel@tonic-gate caddr_t tnva = (caddr_t)P2ROUNDUP((uintptr_t)nva, pgsz); 2147c478bd9Sstevel@tonic-gate size = tnva - p->p_brkbase; 2157c478bd9Sstevel@tonic-gate if (tnva < p->p_brkbase || (size > p->p_brksize && 2167c478bd9Sstevel@tonic-gate size > (size_t)as_rctl)) { 2177c478bd9Sstevel@tonic-gate szc = 0; 2187c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 2197c478bd9Sstevel@tonic-gate size = nva - p->p_brkbase; 2207c478bd9Sstevel@tonic-gate } 2217c478bd9Sstevel@tonic-gate } else { 2227c478bd9Sstevel@tonic-gate size = nva - p->p_brkbase; 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate /* 2267c478bd9Sstevel@tonic-gate * use PAGESIZE to roundup ova because we want to know the real value 2277c478bd9Sstevel@tonic-gate * of the current heap end in case p_brkpageszc changes since the last 2287c478bd9Sstevel@tonic-gate * p_brksize was computed. 2297c478bd9Sstevel@tonic-gate */ 2307c478bd9Sstevel@tonic-gate nva = (caddr_t)P2ROUNDUP((uintptr_t)nva, pgsz); 2317c478bd9Sstevel@tonic-gate ova = (caddr_t)P2ROUNDUP((uintptr_t)(p->p_brkbase + p->p_brksize), 2327c478bd9Sstevel@tonic-gate PAGESIZE); 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate if ((nva < p->p_brkbase) || (size > p->p_brksize && 2357c478bd9Sstevel@tonic-gate size > as_rctl)) { 2367c478bd9Sstevel@tonic-gate mutex_enter(&p->p_lock); 2377c478bd9Sstevel@tonic-gate (void) rctl_action(rctlproc_legacy[RLIMIT_DATA], p->p_rctls, p, 2387c478bd9Sstevel@tonic-gate RCA_SAFE); 2397c478bd9Sstevel@tonic-gate mutex_exit(&p->p_lock); 2407c478bd9Sstevel@tonic-gate return (ENOMEM); 2417c478bd9Sstevel@tonic-gate } 2427c478bd9Sstevel@tonic-gate 2437c478bd9Sstevel@tonic-gate if (nva > ova) { 2447c478bd9Sstevel@tonic-gate struct segvn_crargs crargs = 2457c478bd9Sstevel@tonic-gate SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gate if (!(p->p_datprot & PROT_EXEC)) { 2487c478bd9Sstevel@tonic-gate crargs.prot &= ~PROT_EXEC; 2497c478bd9Sstevel@tonic-gate } 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate /* 2527c478bd9Sstevel@tonic-gate * Add new zfod mapping to extend UNIX data segment 253ec25b48fSsusans * AS_MAP_NO_LPOOB means use 0, and don't reapply OOB policies 254ec25b48fSsusans * via map_pgszcvec(). Use AS_MAP_HEAP to get intermediate 255ec25b48fSsusans * page sizes if ova is not aligned to szc's pgsz. 2567c478bd9Sstevel@tonic-gate */ 257ec25b48fSsusans if (szc > 0) { 258ec25b48fSsusans caddr_t rbss; 259ec25b48fSsusans 260ec25b48fSsusans rbss = (caddr_t)P2ROUNDUP((uintptr_t)p->p_bssbase, 261ec25b48fSsusans pgsz); 262ec25b48fSsusans if (IS_P2ALIGNED(p->p_bssbase, pgsz) || ova > rbss) { 263ec25b48fSsusans crargs.szc = p->p_brkpageszc ? p->p_brkpageszc : 264ec25b48fSsusans AS_MAP_NO_LPOOB; 265ec25b48fSsusans } else if (ova == rbss) { 266ec25b48fSsusans crargs.szc = szc; 267ec25b48fSsusans } else { 268ec25b48fSsusans crargs.szc = AS_MAP_HEAP; 269ec25b48fSsusans } 270ec25b48fSsusans } else { 271ec25b48fSsusans crargs.szc = AS_MAP_NO_LPOOB; 272ec25b48fSsusans } 2737c478bd9Sstevel@tonic-gate crargs.lgrp_mem_policy_flags = LGRP_MP_FLAG_EXTEND_UP; 2747c478bd9Sstevel@tonic-gate error = as_map(as, ova, (size_t)(nva - ova), segvn_create, 2757c478bd9Sstevel@tonic-gate &crargs); 2767c478bd9Sstevel@tonic-gate if (error) { 2777c478bd9Sstevel@tonic-gate return (error); 2787c478bd9Sstevel@tonic-gate } 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate } else if (nva < ova) { 2817c478bd9Sstevel@tonic-gate /* 2827c478bd9Sstevel@tonic-gate * Release mapping to shrink UNIX data segment. 2837c478bd9Sstevel@tonic-gate */ 2847c478bd9Sstevel@tonic-gate (void) as_unmap(as, nva, (size_t)(ova - nva)); 2857c478bd9Sstevel@tonic-gate } 2867c478bd9Sstevel@tonic-gate p->p_brksize = size; 2877c478bd9Sstevel@tonic-gate return (0); 2887c478bd9Sstevel@tonic-gate } 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate /* 2917c478bd9Sstevel@tonic-gate * Grow the stack to include sp. Return 1 if successful, 0 otherwise. 2927c478bd9Sstevel@tonic-gate * This routine assumes that the stack grows downward. 2937c478bd9Sstevel@tonic-gate */ 2947c478bd9Sstevel@tonic-gate int 2957c478bd9Sstevel@tonic-gate grow(caddr_t sp) 2967c478bd9Sstevel@tonic-gate { 2977c478bd9Sstevel@tonic-gate struct proc *p = curproc; 298ec25b48fSsusans struct as *as = p->p_as; 299ec25b48fSsusans size_t oldsize = p->p_stksize; 300ec25b48fSsusans size_t newsize; 3017c478bd9Sstevel@tonic-gate int err; 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate /* 3047c478bd9Sstevel@tonic-gate * Serialize grow operations on an address space. 3057c478bd9Sstevel@tonic-gate * This also serves as the lock protecting p_stksize 3067c478bd9Sstevel@tonic-gate * and p_stkpageszc. 3077c478bd9Sstevel@tonic-gate */ 308ec25b48fSsusans as_rangelock(as); 3097c478bd9Sstevel@tonic-gate if (use_stk_lpg && (p->p_flag & SAUTOLPG) != 0) { 3107c478bd9Sstevel@tonic-gate err = grow_lpg(sp); 3117c478bd9Sstevel@tonic-gate } else { 3127c478bd9Sstevel@tonic-gate err = grow_internal(sp, p->p_stkpageszc); 3137c478bd9Sstevel@tonic-gate } 314ec25b48fSsusans as_rangeunlock(as); 315ec25b48fSsusans 316ec25b48fSsusans if (err == 0 && (newsize = p->p_stksize) > oldsize) { 317ec25b48fSsusans ASSERT(IS_P2ALIGNED(oldsize, PAGESIZE)); 318ec25b48fSsusans ASSERT(IS_P2ALIGNED(newsize, PAGESIZE)); 319ec25b48fSsusans /* 320ec25b48fSsusans * Set up translations so the process doesn't have to fault in 321ec25b48fSsusans * the stack pages we just gave it. 322ec25b48fSsusans */ 323ec25b48fSsusans (void) as_fault(as->a_hat, as, p->p_usrstack - newsize, 324ec25b48fSsusans newsize - oldsize, F_INVAL, S_WRITE); 325ec25b48fSsusans } 3267c478bd9Sstevel@tonic-gate return ((err == 0 ? 1 : 0)); 3277c478bd9Sstevel@tonic-gate } 3287c478bd9Sstevel@tonic-gate 3297c478bd9Sstevel@tonic-gate /* 3307c478bd9Sstevel@tonic-gate * Algorithm: call arch-specific map_pgsz to get best page size to use, 3317c478bd9Sstevel@tonic-gate * then call grow_internal(). 3327c478bd9Sstevel@tonic-gate * Returns 0 on success. 3337c478bd9Sstevel@tonic-gate */ 3347c478bd9Sstevel@tonic-gate static int 3357c478bd9Sstevel@tonic-gate grow_lpg(caddr_t sp) 3367c478bd9Sstevel@tonic-gate { 3377c478bd9Sstevel@tonic-gate struct proc *p = curproc; 3387c478bd9Sstevel@tonic-gate size_t pgsz; 3397c478bd9Sstevel@tonic-gate size_t len, newsize; 340ec25b48fSsusans caddr_t addr, saddr; 341ec25b48fSsusans caddr_t growend; 3427c478bd9Sstevel@tonic-gate int oszc, szc; 3437c478bd9Sstevel@tonic-gate int err; 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate newsize = p->p_usrstack - sp; 3467c478bd9Sstevel@tonic-gate 3477c478bd9Sstevel@tonic-gate oszc = p->p_stkpageszc; 348ec25b48fSsusans pgsz = map_pgsz(MAPPGSZ_STK, p, sp, newsize, 0); 3497c478bd9Sstevel@tonic-gate szc = page_szc(pgsz); 3507c478bd9Sstevel@tonic-gate 3517c478bd9Sstevel@tonic-gate /* 3527c478bd9Sstevel@tonic-gate * Covers two cases: 3537c478bd9Sstevel@tonic-gate * 1. page_szc() returns -1 for invalid page size, so we want to 3547c478bd9Sstevel@tonic-gate * ignore it in that case. 3557c478bd9Sstevel@tonic-gate * 2. By design we never decrease page size, as it is more stable. 3567c478bd9Sstevel@tonic-gate * This shouldn't happen as the stack never shrinks. 3577c478bd9Sstevel@tonic-gate */ 3587c478bd9Sstevel@tonic-gate if (szc <= oszc) { 3597c478bd9Sstevel@tonic-gate err = grow_internal(sp, oszc); 3607c478bd9Sstevel@tonic-gate /* failed, fall back to base page size */ 3617c478bd9Sstevel@tonic-gate if (err != 0 && oszc != 0) { 3627c478bd9Sstevel@tonic-gate err = grow_internal(sp, 0); 3637c478bd9Sstevel@tonic-gate } 3647c478bd9Sstevel@tonic-gate return (err); 3657c478bd9Sstevel@tonic-gate } 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate /* 3687c478bd9Sstevel@tonic-gate * We've grown sufficiently to switch to a new page size. 369ec25b48fSsusans * So we are going to remap the whole segment with the new page size. 3707c478bd9Sstevel@tonic-gate */ 3717c478bd9Sstevel@tonic-gate err = grow_internal(sp, szc); 3727c478bd9Sstevel@tonic-gate /* The grow with szc failed, so fall back to base page size. */ 3737c478bd9Sstevel@tonic-gate if (err != 0) { 3747c478bd9Sstevel@tonic-gate if (szc != 0) { 3757c478bd9Sstevel@tonic-gate err = grow_internal(sp, 0); 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate return (err); 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate 380ec25b48fSsusans /* 381ec25b48fSsusans * Round up stack pointer to a large page boundary and remap 382ec25b48fSsusans * any pgsz pages in the segment already faulted in beyond that 383ec25b48fSsusans * point. 384ec25b48fSsusans */ 385ec25b48fSsusans saddr = p->p_usrstack - p->p_stksize; 386ec25b48fSsusans addr = (caddr_t)P2ROUNDUP((uintptr_t)saddr, pgsz); 387ec25b48fSsusans growend = (caddr_t)P2ALIGN((uintptr_t)p->p_usrstack, pgsz); 388ec25b48fSsusans len = growend - addr; 389ec25b48fSsusans /* Check that len is not negative. Update page size code for stack. */ 390ec25b48fSsusans if (addr >= saddr && growend > addr && IS_P2ALIGNED(len, pgsz)) { 3917c478bd9Sstevel@tonic-gate (void) as_setpagesize(p->p_as, addr, len, szc, B_FALSE); 392ec25b48fSsusans p->p_stkpageszc = szc; 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate ASSERT(err == 0); 3967c478bd9Sstevel@tonic-gate return (err); /* should always be 0 */ 3977c478bd9Sstevel@tonic-gate } 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate /* 4007c478bd9Sstevel@tonic-gate * This routine assumes that the stack grows downward. 4017c478bd9Sstevel@tonic-gate * Returns 0 on success, errno on failure. 4027c478bd9Sstevel@tonic-gate */ 4037c478bd9Sstevel@tonic-gate int 4047c478bd9Sstevel@tonic-gate grow_internal(caddr_t sp, uint_t growszc) 4057c478bd9Sstevel@tonic-gate { 4067c478bd9Sstevel@tonic-gate struct proc *p = curproc; 407ec25b48fSsusans size_t newsize; 4087c478bd9Sstevel@tonic-gate size_t oldsize; 4097c478bd9Sstevel@tonic-gate int error; 4107c478bd9Sstevel@tonic-gate size_t pgsz; 4117c478bd9Sstevel@tonic-gate uint_t szc; 4127c478bd9Sstevel@tonic-gate struct segvn_crargs crargs = SEGVN_ZFOD_ARGS(PROT_ZFOD, PROT_ALL); 4137c478bd9Sstevel@tonic-gate 4147c478bd9Sstevel@tonic-gate ASSERT(sp < p->p_usrstack); 415ec25b48fSsusans sp = (caddr_t)P2ALIGN((uintptr_t)sp, PAGESIZE); 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate /* 4187c478bd9Sstevel@tonic-gate * grow to growszc alignment but use current p->p_stkpageszc for 4197c478bd9Sstevel@tonic-gate * the segvn_crargs szc passed to segvn_create. For memcntl to 4207c478bd9Sstevel@tonic-gate * increase the szc, this allows the new extension segment to be 4217c478bd9Sstevel@tonic-gate * concatenated successfully with the existing stack segment. 4227c478bd9Sstevel@tonic-gate */ 4237c478bd9Sstevel@tonic-gate if ((szc = growszc) != 0) { 4247c478bd9Sstevel@tonic-gate pgsz = page_get_pagesize(szc); 4257c478bd9Sstevel@tonic-gate ASSERT(pgsz > PAGESIZE); 426ec25b48fSsusans newsize = p->p_usrstack - (caddr_t)P2ALIGN((uintptr_t)sp, pgsz); 4277c478bd9Sstevel@tonic-gate if (newsize > (size_t)p->p_stk_ctl) { 4287c478bd9Sstevel@tonic-gate szc = 0; 4297c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 4307c478bd9Sstevel@tonic-gate newsize = p->p_usrstack - sp; 4317c478bd9Sstevel@tonic-gate } 4327c478bd9Sstevel@tonic-gate } else { 4337c478bd9Sstevel@tonic-gate pgsz = PAGESIZE; 434ec25b48fSsusans newsize = p->p_usrstack - sp; 4357c478bd9Sstevel@tonic-gate } 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate if (newsize > (size_t)p->p_stk_ctl) { 4387c478bd9Sstevel@tonic-gate (void) rctl_action(rctlproc_legacy[RLIMIT_STACK], p->p_rctls, p, 4397c478bd9Sstevel@tonic-gate RCA_UNSAFE_ALL); 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate return (ENOMEM); 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate oldsize = p->p_stksize; 4457c478bd9Sstevel@tonic-gate ASSERT(P2PHASE(oldsize, PAGESIZE) == 0); 4467c478bd9Sstevel@tonic-gate 4477c478bd9Sstevel@tonic-gate if (newsize <= oldsize) { /* prevent the stack from shrinking */ 4487c478bd9Sstevel@tonic-gate return (0); 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate if (!(p->p_stkprot & PROT_EXEC)) { 4527c478bd9Sstevel@tonic-gate crargs.prot &= ~PROT_EXEC; 4537c478bd9Sstevel@tonic-gate } 4547c478bd9Sstevel@tonic-gate /* 455ec25b48fSsusans * extend stack with the proposed new growszc, which is different 456ec25b48fSsusans * than p_stkpageszc only on a memcntl to increase the stack pagesize. 457ec25b48fSsusans * AS_MAP_NO_LPOOB means use 0, and don't reapply OOB policies via 458ec25b48fSsusans * map_pgszcvec(). Use AS_MAP_STACK to get intermediate page sizes 459ec25b48fSsusans * if not aligned to szc's pgsz. 4607c478bd9Sstevel@tonic-gate */ 461ec25b48fSsusans if (szc > 0) { 462ec25b48fSsusans caddr_t oldsp = p->p_usrstack - oldsize; 463ec25b48fSsusans caddr_t austk = (caddr_t)P2ALIGN((uintptr_t)p->p_usrstack, 464ec25b48fSsusans pgsz); 465ec25b48fSsusans 466ec25b48fSsusans if (IS_P2ALIGNED(p->p_usrstack, pgsz) || oldsp < austk) { 467ec25b48fSsusans crargs.szc = p->p_stkpageszc ? p->p_stkpageszc : 468ec25b48fSsusans AS_MAP_NO_LPOOB; 469ec25b48fSsusans } else if (oldsp == austk) { 470ec25b48fSsusans crargs.szc = szc; 471ec25b48fSsusans } else { 472ec25b48fSsusans crargs.szc = AS_MAP_STACK; 473ec25b48fSsusans } 474ec25b48fSsusans } else { 475ec25b48fSsusans crargs.szc = AS_MAP_NO_LPOOB; 476ec25b48fSsusans } 4777c478bd9Sstevel@tonic-gate crargs.lgrp_mem_policy_flags = LGRP_MP_FLAG_EXTEND_DOWN; 4787c478bd9Sstevel@tonic-gate 479ec25b48fSsusans if ((error = as_map(p->p_as, p->p_usrstack - newsize, newsize - oldsize, 4807c478bd9Sstevel@tonic-gate segvn_create, &crargs)) != 0) { 4817c478bd9Sstevel@tonic-gate if (error == EAGAIN) { 4827c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "Sorry, no swap space to grow stack " 483*ae115bc7Smrj "for pid %d (%s)", p->p_pid, PTOU(p)->u_comm); 4847c478bd9Sstevel@tonic-gate } 4857c478bd9Sstevel@tonic-gate return (error); 4867c478bd9Sstevel@tonic-gate } 4877c478bd9Sstevel@tonic-gate p->p_stksize = newsize; 4887c478bd9Sstevel@tonic-gate return (0); 4897c478bd9Sstevel@tonic-gate } 4907c478bd9Sstevel@tonic-gate 4917c478bd9Sstevel@tonic-gate /* 4927c478bd9Sstevel@tonic-gate * Used for MAP_ANON - fast way to get anonymous pages 4937c478bd9Sstevel@tonic-gate */ 4947c478bd9Sstevel@tonic-gate static int 4957c478bd9Sstevel@tonic-gate zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags, 4967c478bd9Sstevel@tonic-gate offset_t pos) 4977c478bd9Sstevel@tonic-gate { 498ec25b48fSsusans struct segvn_crargs vn_a; 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate if (((PROT_ALL & uprot) != uprot)) 5017c478bd9Sstevel@tonic-gate return (EACCES); 5027c478bd9Sstevel@tonic-gate 5037c478bd9Sstevel@tonic-gate if ((flags & MAP_FIXED) != 0) { 5047c478bd9Sstevel@tonic-gate caddr_t userlimit; 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate /* 5077c478bd9Sstevel@tonic-gate * Use the user address. First verify that 5087c478bd9Sstevel@tonic-gate * the address to be used is page aligned. 5097c478bd9Sstevel@tonic-gate * Then make some simple bounds checks. 5107c478bd9Sstevel@tonic-gate */ 5117c478bd9Sstevel@tonic-gate if (((uintptr_t)*addrp & PAGEOFFSET) != 0) 5127c478bd9Sstevel@tonic-gate return (EINVAL); 5137c478bd9Sstevel@tonic-gate 5147c478bd9Sstevel@tonic-gate userlimit = flags & _MAP_LOW32 ? 5157c478bd9Sstevel@tonic-gate (caddr_t)USERLIMIT32 : as->a_userlimit; 5167c478bd9Sstevel@tonic-gate switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) { 5177c478bd9Sstevel@tonic-gate case RANGE_OKAY: 5187c478bd9Sstevel@tonic-gate break; 5197c478bd9Sstevel@tonic-gate case RANGE_BADPROT: 5207c478bd9Sstevel@tonic-gate return (ENOTSUP); 5217c478bd9Sstevel@tonic-gate case RANGE_BADADDR: 5227c478bd9Sstevel@tonic-gate default: 5237c478bd9Sstevel@tonic-gate return (ENOMEM); 5247c478bd9Sstevel@tonic-gate } 5257c478bd9Sstevel@tonic-gate (void) as_unmap(as, *addrp, len); 5267c478bd9Sstevel@tonic-gate } else { 5277c478bd9Sstevel@tonic-gate /* 5287c478bd9Sstevel@tonic-gate * No need to worry about vac alignment for anonymous 5297c478bd9Sstevel@tonic-gate * pages since this is a "clone" object that doesn't 5307c478bd9Sstevel@tonic-gate * yet exist. 5317c478bd9Sstevel@tonic-gate */ 5327c478bd9Sstevel@tonic-gate map_addr(addrp, len, pos, 0, flags); 5337c478bd9Sstevel@tonic-gate if (*addrp == NULL) 5347c478bd9Sstevel@tonic-gate return (ENOMEM); 5357c478bd9Sstevel@tonic-gate } 5367c478bd9Sstevel@tonic-gate 5377c478bd9Sstevel@tonic-gate /* 5387c478bd9Sstevel@tonic-gate * Use the seg_vn segment driver; passing in the NULL amp 5397c478bd9Sstevel@tonic-gate * gives the desired "cloning" effect. 5407c478bd9Sstevel@tonic-gate */ 541ec25b48fSsusans vn_a.vp = NULL; 542ec25b48fSsusans vn_a.offset = 0; 543ec25b48fSsusans vn_a.type = flags & MAP_TYPE; 544ec25b48fSsusans vn_a.prot = uprot; 545ec25b48fSsusans vn_a.maxprot = PROT_ALL; 546ec25b48fSsusans vn_a.flags = flags & ~MAP_TYPE; 547ec25b48fSsusans vn_a.cred = CRED(); 548ec25b48fSsusans vn_a.amp = NULL; 549ec25b48fSsusans vn_a.szc = 0; 550ec25b48fSsusans vn_a.lgrp_mem_policy_flags = 0; 551ec25b48fSsusans 552ec25b48fSsusans return (as_map(as, *addrp, len, segvn_create, &vn_a)); 5537c478bd9Sstevel@tonic-gate } 5547c478bd9Sstevel@tonic-gate 5557c478bd9Sstevel@tonic-gate static int 5567c478bd9Sstevel@tonic-gate smmap_common(caddr_t *addrp, size_t len, 5577c478bd9Sstevel@tonic-gate int prot, int flags, struct file *fp, offset_t pos) 5587c478bd9Sstevel@tonic-gate { 5597c478bd9Sstevel@tonic-gate struct vnode *vp; 5607c478bd9Sstevel@tonic-gate struct as *as = curproc->p_as; 5617c478bd9Sstevel@tonic-gate uint_t uprot, maxprot, type; 5627c478bd9Sstevel@tonic-gate int error; 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate if ((flags & ~(MAP_SHARED | MAP_PRIVATE | MAP_FIXED | _MAP_NEW | 5657c478bd9Sstevel@tonic-gate _MAP_LOW32 | MAP_NORESERVE | MAP_ANON | MAP_ALIGN | 5667c478bd9Sstevel@tonic-gate MAP_TEXT | MAP_INITDATA)) != 0) { 5677c478bd9Sstevel@tonic-gate /* | MAP_RENAME */ /* not implemented, let user know */ 5687c478bd9Sstevel@tonic-gate return (EINVAL); 5697c478bd9Sstevel@tonic-gate } 5707c478bd9Sstevel@tonic-gate 5717c478bd9Sstevel@tonic-gate if ((flags & MAP_TEXT) && !(prot & PROT_EXEC)) { 5727c478bd9Sstevel@tonic-gate return (EINVAL); 5737c478bd9Sstevel@tonic-gate } 5747c478bd9Sstevel@tonic-gate 5757c478bd9Sstevel@tonic-gate if ((flags & (MAP_TEXT | MAP_INITDATA)) == (MAP_TEXT | MAP_INITDATA)) { 5767c478bd9Sstevel@tonic-gate return (EINVAL); 5777c478bd9Sstevel@tonic-gate } 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate #if defined(__sparc) 5807c478bd9Sstevel@tonic-gate /* 5817c478bd9Sstevel@tonic-gate * See if this is an "old mmap call". If so, remember this 5827c478bd9Sstevel@tonic-gate * fact and convert the flags value given to mmap to indicate 5837c478bd9Sstevel@tonic-gate * the specified address in the system call must be used. 5847c478bd9Sstevel@tonic-gate * _MAP_NEW is turned set by all new uses of mmap. 5857c478bd9Sstevel@tonic-gate */ 5867c478bd9Sstevel@tonic-gate if ((flags & _MAP_NEW) == 0) 5877c478bd9Sstevel@tonic-gate flags |= MAP_FIXED; 5887c478bd9Sstevel@tonic-gate #endif 5897c478bd9Sstevel@tonic-gate flags &= ~_MAP_NEW; 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate type = flags & MAP_TYPE; 5927c478bd9Sstevel@tonic-gate if (type != MAP_PRIVATE && type != MAP_SHARED) 5937c478bd9Sstevel@tonic-gate return (EINVAL); 5947c478bd9Sstevel@tonic-gate 5957c478bd9Sstevel@tonic-gate 5967c478bd9Sstevel@tonic-gate if (flags & MAP_ALIGN) { 5977c478bd9Sstevel@tonic-gate 5987c478bd9Sstevel@tonic-gate if (flags & MAP_FIXED) 5997c478bd9Sstevel@tonic-gate return (EINVAL); 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate /* alignment needs to be a power of 2 >= page size */ 6027c478bd9Sstevel@tonic-gate if (((uintptr_t)*addrp < PAGESIZE && (uintptr_t)*addrp != 0) || 6037c478bd9Sstevel@tonic-gate !ISP2((uintptr_t)*addrp)) 6047c478bd9Sstevel@tonic-gate return (EINVAL); 6057c478bd9Sstevel@tonic-gate } 6067c478bd9Sstevel@tonic-gate /* 6077c478bd9Sstevel@tonic-gate * Check for bad lengths and file position. 6087c478bd9Sstevel@tonic-gate * We let the VOP_MAP routine check for negative lengths 6097c478bd9Sstevel@tonic-gate * since on some vnode types this might be appropriate. 6107c478bd9Sstevel@tonic-gate */ 6117c478bd9Sstevel@tonic-gate if (len == 0 || (pos & (u_offset_t)PAGEOFFSET) != 0) 6127c478bd9Sstevel@tonic-gate return (EINVAL); 6137c478bd9Sstevel@tonic-gate 6147c478bd9Sstevel@tonic-gate maxprot = PROT_ALL; /* start out allowing all accesses */ 6157c478bd9Sstevel@tonic-gate uprot = prot | PROT_USER; 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate if (fp == NULL) { 6187c478bd9Sstevel@tonic-gate ASSERT(flags & MAP_ANON); 6197c478bd9Sstevel@tonic-gate as_rangelock(as); 6207c478bd9Sstevel@tonic-gate error = zmap(as, addrp, len, uprot, flags, pos); 6217c478bd9Sstevel@tonic-gate as_rangeunlock(as); 6227c478bd9Sstevel@tonic-gate return (error); 6237c478bd9Sstevel@tonic-gate } else if ((flags & MAP_ANON) != 0) 6247c478bd9Sstevel@tonic-gate return (EINVAL); 6257c478bd9Sstevel@tonic-gate 6267c478bd9Sstevel@tonic-gate vp = fp->f_vnode; 6277c478bd9Sstevel@tonic-gate 6287c478bd9Sstevel@tonic-gate /* Can't execute code from "noexec" mounted filesystem. */ 6297c478bd9Sstevel@tonic-gate if ((vp->v_vfsp->vfs_flag & VFS_NOEXEC) != 0) 6307c478bd9Sstevel@tonic-gate maxprot &= ~PROT_EXEC; 6317c478bd9Sstevel@tonic-gate 6327c478bd9Sstevel@tonic-gate /* 6337c478bd9Sstevel@tonic-gate * These checks were added as part of large files. 6347c478bd9Sstevel@tonic-gate * 63549a63d68Speterte * Return ENXIO if the initial position is negative; return EOVERFLOW 6367c478bd9Sstevel@tonic-gate * if (offset + len) would overflow the maximum allowed offset for the 6377c478bd9Sstevel@tonic-gate * type of file descriptor being used. 6387c478bd9Sstevel@tonic-gate */ 6397c478bd9Sstevel@tonic-gate if (vp->v_type == VREG) { 64049a63d68Speterte if (pos < 0) 64149a63d68Speterte return (ENXIO); 6427c478bd9Sstevel@tonic-gate if ((offset_t)len > (OFFSET_MAX(fp) - pos)) 6437c478bd9Sstevel@tonic-gate return (EOVERFLOW); 6447c478bd9Sstevel@tonic-gate } 6457c478bd9Sstevel@tonic-gate 6467c478bd9Sstevel@tonic-gate if (type == MAP_SHARED && (fp->f_flag & FWRITE) == 0) { 6477c478bd9Sstevel@tonic-gate /* no write access allowed */ 6487c478bd9Sstevel@tonic-gate maxprot &= ~PROT_WRITE; 6497c478bd9Sstevel@tonic-gate } 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate /* 6527c478bd9Sstevel@tonic-gate * XXX - Do we also adjust maxprot based on protections 6537c478bd9Sstevel@tonic-gate * of the vnode? E.g. if no execute permission is given 6547c478bd9Sstevel@tonic-gate * on the vnode for the current user, maxprot probably 6557c478bd9Sstevel@tonic-gate * should disallow PROT_EXEC also? This is different 6567c478bd9Sstevel@tonic-gate * from the write access as this would be a per vnode 6577c478bd9Sstevel@tonic-gate * test as opposed to a per fd test for writability. 6587c478bd9Sstevel@tonic-gate */ 6597c478bd9Sstevel@tonic-gate 6607c478bd9Sstevel@tonic-gate /* 6617c478bd9Sstevel@tonic-gate * Verify that the specified protections are not greater than 6627c478bd9Sstevel@tonic-gate * the maximum allowable protections. Also test to make sure 6637c478bd9Sstevel@tonic-gate * that the file descriptor does allows for read access since 6647c478bd9Sstevel@tonic-gate * "write only" mappings are hard to do since normally we do 6657c478bd9Sstevel@tonic-gate * the read from the file before the page can be written. 6667c478bd9Sstevel@tonic-gate */ 6677c478bd9Sstevel@tonic-gate if (((maxprot & uprot) != uprot) || (fp->f_flag & FREAD) == 0) 6687c478bd9Sstevel@tonic-gate return (EACCES); 6697c478bd9Sstevel@tonic-gate 6707c478bd9Sstevel@tonic-gate /* 6717c478bd9Sstevel@tonic-gate * If the user specified an address, do some simple checks here 6727c478bd9Sstevel@tonic-gate */ 6737c478bd9Sstevel@tonic-gate if ((flags & MAP_FIXED) != 0) { 6747c478bd9Sstevel@tonic-gate caddr_t userlimit; 6757c478bd9Sstevel@tonic-gate 6767c478bd9Sstevel@tonic-gate /* 6777c478bd9Sstevel@tonic-gate * Use the user address. First verify that 6787c478bd9Sstevel@tonic-gate * the address to be used is page aligned. 6797c478bd9Sstevel@tonic-gate * Then make some simple bounds checks. 6807c478bd9Sstevel@tonic-gate */ 6817c478bd9Sstevel@tonic-gate if (((uintptr_t)*addrp & PAGEOFFSET) != 0) 6827c478bd9Sstevel@tonic-gate return (EINVAL); 6837c478bd9Sstevel@tonic-gate 6847c478bd9Sstevel@tonic-gate userlimit = flags & _MAP_LOW32 ? 6857c478bd9Sstevel@tonic-gate (caddr_t)USERLIMIT32 : as->a_userlimit; 6867c478bd9Sstevel@tonic-gate switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) { 6877c478bd9Sstevel@tonic-gate case RANGE_OKAY: 6887c478bd9Sstevel@tonic-gate break; 6897c478bd9Sstevel@tonic-gate case RANGE_BADPROT: 6907c478bd9Sstevel@tonic-gate return (ENOTSUP); 6917c478bd9Sstevel@tonic-gate case RANGE_BADADDR: 6927c478bd9Sstevel@tonic-gate default: 6937c478bd9Sstevel@tonic-gate return (ENOMEM); 6947c478bd9Sstevel@tonic-gate } 6957c478bd9Sstevel@tonic-gate } 6967c478bd9Sstevel@tonic-gate 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate /* 6997c478bd9Sstevel@tonic-gate * Ok, now let the vnode map routine do its thing to set things up. 7007c478bd9Sstevel@tonic-gate */ 7017c478bd9Sstevel@tonic-gate error = VOP_MAP(vp, pos, as, 7027c478bd9Sstevel@tonic-gate addrp, len, uprot, maxprot, flags, fp->f_cred); 7037c478bd9Sstevel@tonic-gate 7047c478bd9Sstevel@tonic-gate if (error == 0) { 7057c478bd9Sstevel@tonic-gate if (vp->v_type == VREG && 7067c478bd9Sstevel@tonic-gate (flags & (MAP_TEXT | MAP_INITDATA)) != 0) { 7077c478bd9Sstevel@tonic-gate /* 7087c478bd9Sstevel@tonic-gate * Mark this as an executable vnode 7097c478bd9Sstevel@tonic-gate */ 7107c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 7117c478bd9Sstevel@tonic-gate vp->v_flag |= VVMEXEC; 7127c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 7137c478bd9Sstevel@tonic-gate } 7147c478bd9Sstevel@tonic-gate } 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate return (error); 7177c478bd9Sstevel@tonic-gate } 7187c478bd9Sstevel@tonic-gate 7197c478bd9Sstevel@tonic-gate #ifdef _LP64 7207c478bd9Sstevel@tonic-gate /* 7217c478bd9Sstevel@tonic-gate * LP64 mmap(2) system call: 64-bit offset, 64-bit address. 7227c478bd9Sstevel@tonic-gate * 7237c478bd9Sstevel@tonic-gate * The "large file" mmap routine mmap64(2) is also mapped to this routine 7247c478bd9Sstevel@tonic-gate * by the 64-bit version of libc. 7257c478bd9Sstevel@tonic-gate * 7267c478bd9Sstevel@tonic-gate * Eventually, this should be the only version, and have smmap_common() 7277c478bd9Sstevel@tonic-gate * folded back into it again. Some day. 7287c478bd9Sstevel@tonic-gate */ 7297c478bd9Sstevel@tonic-gate caddr_t 7307c478bd9Sstevel@tonic-gate smmap64(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos) 7317c478bd9Sstevel@tonic-gate { 7327c478bd9Sstevel@tonic-gate struct file *fp; 7337c478bd9Sstevel@tonic-gate int error; 7347c478bd9Sstevel@tonic-gate 7357c478bd9Sstevel@tonic-gate if (flags & _MAP_LOW32) 7367c478bd9Sstevel@tonic-gate error = EINVAL; 7377c478bd9Sstevel@tonic-gate else if (fd == -1 && (flags & MAP_ANON) != 0) 7387c478bd9Sstevel@tonic-gate error = smmap_common(&addr, len, prot, flags, 7397c478bd9Sstevel@tonic-gate NULL, (offset_t)pos); 7407c478bd9Sstevel@tonic-gate else if ((fp = getf(fd)) != NULL) { 7417c478bd9Sstevel@tonic-gate error = smmap_common(&addr, len, prot, flags, 7427c478bd9Sstevel@tonic-gate fp, (offset_t)pos); 7437c478bd9Sstevel@tonic-gate releasef(fd); 7447c478bd9Sstevel@tonic-gate } else 7457c478bd9Sstevel@tonic-gate error = EBADF; 7467c478bd9Sstevel@tonic-gate 7477c478bd9Sstevel@tonic-gate return (error ? (caddr_t)(uintptr_t)set_errno(error) : addr); 7487c478bd9Sstevel@tonic-gate } 7497c478bd9Sstevel@tonic-gate #endif /* _LP64 */ 7507c478bd9Sstevel@tonic-gate 7517c478bd9Sstevel@tonic-gate #if defined(_SYSCALL32_IMPL) || defined(_ILP32) 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate /* 7547c478bd9Sstevel@tonic-gate * ILP32 mmap(2) system call: 32-bit offset, 32-bit address. 7557c478bd9Sstevel@tonic-gate */ 7567c478bd9Sstevel@tonic-gate caddr_t 7577c478bd9Sstevel@tonic-gate smmap32(caddr32_t addr, size32_t len, int prot, int flags, int fd, off32_t pos) 7587c478bd9Sstevel@tonic-gate { 7597c478bd9Sstevel@tonic-gate struct file *fp; 7607c478bd9Sstevel@tonic-gate int error; 7617c478bd9Sstevel@tonic-gate caddr_t a = (caddr_t)(uintptr_t)addr; 7627c478bd9Sstevel@tonic-gate 7637c478bd9Sstevel@tonic-gate if (flags & _MAP_LOW32) 7647c478bd9Sstevel@tonic-gate error = EINVAL; 7657c478bd9Sstevel@tonic-gate else if (fd == -1 && (flags & MAP_ANON) != 0) 7667c478bd9Sstevel@tonic-gate error = smmap_common(&a, (size_t)len, prot, 7677c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, NULL, (offset_t)pos); 7687c478bd9Sstevel@tonic-gate else if ((fp = getf(fd)) != NULL) { 7697c478bd9Sstevel@tonic-gate error = smmap_common(&a, (size_t)len, prot, 7707c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, fp, (offset_t)pos); 7717c478bd9Sstevel@tonic-gate releasef(fd); 7727c478bd9Sstevel@tonic-gate } else 7737c478bd9Sstevel@tonic-gate error = EBADF; 7747c478bd9Sstevel@tonic-gate 7757c478bd9Sstevel@tonic-gate ASSERT(error != 0 || (uintptr_t)(a + len) < (uintptr_t)UINT32_MAX); 7767c478bd9Sstevel@tonic-gate 7777c478bd9Sstevel@tonic-gate return (error ? (caddr_t)(uintptr_t)set_errno(error) : a); 7787c478bd9Sstevel@tonic-gate } 7797c478bd9Sstevel@tonic-gate 7807c478bd9Sstevel@tonic-gate /* 7817c478bd9Sstevel@tonic-gate * ILP32 mmap64(2) system call: 64-bit offset, 32-bit address. 7827c478bd9Sstevel@tonic-gate * 7837c478bd9Sstevel@tonic-gate * Now things really get ugly because we can't use the C-style 7847c478bd9Sstevel@tonic-gate * calling convention for more than 6 args, and 64-bit parameter 7857c478bd9Sstevel@tonic-gate * passing on 32-bit systems is less than clean. 7867c478bd9Sstevel@tonic-gate */ 7877c478bd9Sstevel@tonic-gate 7887c478bd9Sstevel@tonic-gate struct mmaplf32a { 7897c478bd9Sstevel@tonic-gate caddr_t addr; 7907c478bd9Sstevel@tonic-gate size_t len; 7917c478bd9Sstevel@tonic-gate #ifdef _LP64 7927c478bd9Sstevel@tonic-gate /* 7937c478bd9Sstevel@tonic-gate * 32-bit contents, 64-bit cells 7947c478bd9Sstevel@tonic-gate */ 7957c478bd9Sstevel@tonic-gate uint64_t prot; 7967c478bd9Sstevel@tonic-gate uint64_t flags; 7977c478bd9Sstevel@tonic-gate uint64_t fd; 7987c478bd9Sstevel@tonic-gate uint64_t offhi; 7997c478bd9Sstevel@tonic-gate uint64_t offlo; 8007c478bd9Sstevel@tonic-gate #else 8017c478bd9Sstevel@tonic-gate /* 8027c478bd9Sstevel@tonic-gate * 32-bit contents, 32-bit cells 8037c478bd9Sstevel@tonic-gate */ 8047c478bd9Sstevel@tonic-gate uint32_t prot; 8057c478bd9Sstevel@tonic-gate uint32_t flags; 8067c478bd9Sstevel@tonic-gate uint32_t fd; 8077c478bd9Sstevel@tonic-gate uint32_t offhi; 8087c478bd9Sstevel@tonic-gate uint32_t offlo; 8097c478bd9Sstevel@tonic-gate #endif 8107c478bd9Sstevel@tonic-gate }; 8117c478bd9Sstevel@tonic-gate 8127c478bd9Sstevel@tonic-gate int 8137c478bd9Sstevel@tonic-gate smmaplf32(struct mmaplf32a *uap, rval_t *rvp) 8147c478bd9Sstevel@tonic-gate { 8157c478bd9Sstevel@tonic-gate struct file *fp; 8167c478bd9Sstevel@tonic-gate int error; 8177c478bd9Sstevel@tonic-gate caddr_t a = uap->addr; 8187c478bd9Sstevel@tonic-gate int flags = (int)uap->flags; 8197c478bd9Sstevel@tonic-gate int fd = (int)uap->fd; 8207c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN 8217c478bd9Sstevel@tonic-gate offset_t off = ((u_offset_t)uap->offhi << 32) | (u_offset_t)uap->offlo; 8227c478bd9Sstevel@tonic-gate #else 8237c478bd9Sstevel@tonic-gate offset_t off = ((u_offset_t)uap->offlo << 32) | (u_offset_t)uap->offhi; 8247c478bd9Sstevel@tonic-gate #endif 8257c478bd9Sstevel@tonic-gate 8267c478bd9Sstevel@tonic-gate if (flags & _MAP_LOW32) 8277c478bd9Sstevel@tonic-gate error = EINVAL; 8287c478bd9Sstevel@tonic-gate else if (fd == -1 && (flags & MAP_ANON) != 0) 8297c478bd9Sstevel@tonic-gate error = smmap_common(&a, uap->len, (int)uap->prot, 8307c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, NULL, off); 8317c478bd9Sstevel@tonic-gate else if ((fp = getf(fd)) != NULL) { 8327c478bd9Sstevel@tonic-gate error = smmap_common(&a, uap->len, (int)uap->prot, 8337c478bd9Sstevel@tonic-gate flags | _MAP_LOW32, fp, off); 8347c478bd9Sstevel@tonic-gate releasef(fd); 8357c478bd9Sstevel@tonic-gate } else 8367c478bd9Sstevel@tonic-gate error = EBADF; 8377c478bd9Sstevel@tonic-gate 8387c478bd9Sstevel@tonic-gate if (error == 0) 8397c478bd9Sstevel@tonic-gate rvp->r_val1 = (uintptr_t)a; 8407c478bd9Sstevel@tonic-gate return (error); 8417c478bd9Sstevel@tonic-gate } 8427c478bd9Sstevel@tonic-gate 8437c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL || _ILP32 */ 8447c478bd9Sstevel@tonic-gate 8457c478bd9Sstevel@tonic-gate int 8467c478bd9Sstevel@tonic-gate munmap(caddr_t addr, size_t len) 8477c478bd9Sstevel@tonic-gate { 8487c478bd9Sstevel@tonic-gate struct proc *p = curproc; 8497c478bd9Sstevel@tonic-gate struct as *as = p->p_as; 8507c478bd9Sstevel@tonic-gate 8517c478bd9Sstevel@tonic-gate if (((uintptr_t)addr & PAGEOFFSET) != 0 || len == 0) 8527c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate if (valid_usr_range(addr, len, 0, as, as->a_userlimit) != RANGE_OKAY) 8557c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 8567c478bd9Sstevel@tonic-gate 8577c478bd9Sstevel@tonic-gate /* 8587c478bd9Sstevel@tonic-gate * Discard lwpchan mappings. 8597c478bd9Sstevel@tonic-gate */ 8607c478bd9Sstevel@tonic-gate if (p->p_lcp != NULL) 8617c478bd9Sstevel@tonic-gate lwpchan_delete_mapping(p, addr, addr + len); 8627c478bd9Sstevel@tonic-gate if (as_unmap(as, addr, len) != 0) 8637c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 8647c478bd9Sstevel@tonic-gate 8657c478bd9Sstevel@tonic-gate return (0); 8667c478bd9Sstevel@tonic-gate } 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate int 8697c478bd9Sstevel@tonic-gate mprotect(caddr_t addr, size_t len, int prot) 8707c478bd9Sstevel@tonic-gate { 8717c478bd9Sstevel@tonic-gate struct as *as = curproc->p_as; 8727c478bd9Sstevel@tonic-gate uint_t uprot = prot | PROT_USER; 8737c478bd9Sstevel@tonic-gate int error; 8747c478bd9Sstevel@tonic-gate 8757c478bd9Sstevel@tonic-gate if (((uintptr_t)addr & PAGEOFFSET) != 0 || len == 0) 8767c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 8777c478bd9Sstevel@tonic-gate 8787c478bd9Sstevel@tonic-gate switch (valid_usr_range(addr, len, prot, as, as->a_userlimit)) { 8797c478bd9Sstevel@tonic-gate case RANGE_OKAY: 8807c478bd9Sstevel@tonic-gate break; 8817c478bd9Sstevel@tonic-gate case RANGE_BADPROT: 8827c478bd9Sstevel@tonic-gate return (set_errno(ENOTSUP)); 8837c478bd9Sstevel@tonic-gate case RANGE_BADADDR: 8847c478bd9Sstevel@tonic-gate default: 8857c478bd9Sstevel@tonic-gate return (set_errno(ENOMEM)); 8867c478bd9Sstevel@tonic-gate } 8877c478bd9Sstevel@tonic-gate 8887c478bd9Sstevel@tonic-gate error = as_setprot(as, addr, len, uprot); 8897c478bd9Sstevel@tonic-gate if (error) 8907c478bd9Sstevel@tonic-gate return (set_errno(error)); 8917c478bd9Sstevel@tonic-gate return (0); 8927c478bd9Sstevel@tonic-gate } 8937c478bd9Sstevel@tonic-gate 8947c478bd9Sstevel@tonic-gate #define MC_CACHE 128 /* internal result buffer */ 8957c478bd9Sstevel@tonic-gate #define MC_QUANTUM (MC_CACHE * PAGESIZE) /* addresses covered in loop */ 8967c478bd9Sstevel@tonic-gate 8977c478bd9Sstevel@tonic-gate int 8987c478bd9Sstevel@tonic-gate mincore(caddr_t addr, size_t len, char *vecp) 8997c478bd9Sstevel@tonic-gate { 9007c478bd9Sstevel@tonic-gate struct as *as = curproc->p_as; 9017c478bd9Sstevel@tonic-gate caddr_t ea; /* end address of loop */ 9027c478bd9Sstevel@tonic-gate size_t rl; /* inner result length */ 9037c478bd9Sstevel@tonic-gate char vec[MC_CACHE]; /* local vector cache */ 9047c478bd9Sstevel@tonic-gate int error; 9057c478bd9Sstevel@tonic-gate model_t model; 9067c478bd9Sstevel@tonic-gate long llen; 9077c478bd9Sstevel@tonic-gate 9087c478bd9Sstevel@tonic-gate model = get_udatamodel(); 9097c478bd9Sstevel@tonic-gate /* 9107c478bd9Sstevel@tonic-gate * Validate form of address parameters. 9117c478bd9Sstevel@tonic-gate */ 9127c478bd9Sstevel@tonic-gate if (model == DATAMODEL_NATIVE) { 9137c478bd9Sstevel@tonic-gate llen = (long)len; 9147c478bd9Sstevel@tonic-gate } else { 9157c478bd9Sstevel@tonic-gate llen = (int32_t)(size32_t)len; 9167c478bd9Sstevel@tonic-gate } 9177c478bd9Sstevel@tonic-gate if (((uintptr_t)addr & PAGEOFFSET) != 0 || llen <= 0) 9187c478bd9Sstevel@tonic-gate return (set_errno(EINVAL)); 9197c478bd9Sstevel@tonic-gate 9207c478bd9Sstevel@tonic-gate if (valid_usr_range(addr, len, 0, as, as->a_userlimit) != RANGE_OKAY) 9217c478bd9Sstevel@tonic-gate return (set_errno(ENOMEM)); 9227c478bd9Sstevel@tonic-gate 9237c478bd9Sstevel@tonic-gate /* 9247c478bd9Sstevel@tonic-gate * Loop over subranges of interval [addr : addr + len), recovering 9257c478bd9Sstevel@tonic-gate * results internally and then copying them out to caller. Subrange 9267c478bd9Sstevel@tonic-gate * is based on the size of MC_CACHE, defined above. 9277c478bd9Sstevel@tonic-gate */ 9287c478bd9Sstevel@tonic-gate for (ea = addr + len; addr < ea; addr += MC_QUANTUM) { 9297c478bd9Sstevel@tonic-gate error = as_incore(as, addr, 9307c478bd9Sstevel@tonic-gate (size_t)MIN(MC_QUANTUM, ea - addr), vec, &rl); 9317c478bd9Sstevel@tonic-gate if (rl != 0) { 9327c478bd9Sstevel@tonic-gate rl = (rl + PAGESIZE - 1) / PAGESIZE; 9337c478bd9Sstevel@tonic-gate if (copyout(vec, vecp, rl) != 0) 9347c478bd9Sstevel@tonic-gate return (set_errno(EFAULT)); 9357c478bd9Sstevel@tonic-gate vecp += rl; 9367c478bd9Sstevel@tonic-gate } 9377c478bd9Sstevel@tonic-gate if (error != 0) 9387c478bd9Sstevel@tonic-gate return (set_errno(ENOMEM)); 9397c478bd9Sstevel@tonic-gate } 9407c478bd9Sstevel@tonic-gate return (0); 9417c478bd9Sstevel@tonic-gate } 942