1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <errno.h> 29 #include <unistd.h> 30 #include <sys/mman.h> 31 #include <sys/param.h> 32 #include <sys/lx_debug.h> 33 #include <sys/lx_misc.h> 34 35 /* 36 * There are two forms of mmap, mmap() and mmap2(). The only difference is that 37 * the final argument to mmap2() specifies the number of pages, not bytes. 38 * Linux has a number of additional flags, but they are all deprecated. We also 39 * ignore the MAP_GROWSDOWN flag, which has no equivalent on Solaris. 40 * 41 * The Linux mmap() returns ENOMEM in some cases where Solaris returns 42 * EOVERFLOW, so we translate the errno as necessary. 43 */ 44 45 int pagesize; /* needed for mmap2() */ 46 47 #define LX_MAP_ANONYMOUS 0x00020 48 #define LX_MAP_NORESERVE 0x04000 49 50 static int 51 ltos_mmap_flags(int flags) 52 { 53 int new_flags; 54 55 new_flags = flags & (MAP_TYPE | MAP_FIXED); 56 if (flags & LX_MAP_ANONYMOUS) 57 new_flags |= MAP_ANONYMOUS; 58 if (flags & LX_MAP_NORESERVE) 59 new_flags |= MAP_NORESERVE; 60 61 return (new_flags); 62 } 63 64 static int 65 mmap_common(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, 66 uintptr_t p5, off64_t p6) 67 { 68 void *addr = (void *)p1; 69 size_t len = p2; 70 int prot = p3; 71 int flags = p4; 72 int fd = p5; 73 off64_t off = p6; 74 void *ret; 75 76 if (lx_debug_enabled != 0) { 77 char *path, path_buf[MAXPATHLEN]; 78 79 path = lx_fd_to_path(fd, path_buf, sizeof (path_buf)); 80 if (path == NULL) 81 path = "?"; 82 83 lx_debug("\tmmap_common(): fd = %d - %s", fd, path); 84 } 85 86 /* 87 * Under Linux, the file descriptor is ignored when mapping zfod 88 * anonymous memory, On Solaris, we want the fd set to -1 for the 89 * same functionality. 90 */ 91 if (flags & LX_MAP_ANONYMOUS) 92 fd = -1; 93 94 /* 95 * This is totally insane. The NOTES section in the linux mmap(2) man 96 * page claims that on some architectures, read protection may 97 * automatically include exec protection. It has been observed on a 98 * native linux system that the /proc/<pid>/maps file does indeed 99 * show that segments mmap'd from userland (such as libraries mapped in 100 * by the dynamic linker) all have exec the permission set, even for 101 * data segments. 102 */ 103 if (prot & PROT_READ) 104 prot |= PROT_EXEC; 105 106 ret = mmap64(addr, len, prot, ltos_mmap_flags(flags), fd, off); 107 108 if (ret == MAP_FAILED) 109 return (errno == EOVERFLOW ? -ENOMEM : -errno); 110 else 111 return ((int)ret); 112 } 113 114 int 115 lx_mmap(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, 116 uintptr_t p5, uintptr_t p6) 117 { 118 return (mmap_common(p1, p2, p3, p4, p5, (off64_t)p6)); 119 } 120 121 int 122 lx_mmap2(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, 123 uintptr_t p5, uintptr_t p6) 124 { 125 if (pagesize == 0) 126 pagesize = sysconf(_SC_PAGESIZE); 127 128 return (mmap_common(p1, p2, p3, p4, p5, (off64_t)p6 * pagesize)); 129 } 130 131 132 /* 133 * The locking family of system calls, as well as msync(), are identical. On 134 * Solaris, they are layered on top of the memcntl syscall, so they cannot be 135 * pass-thru. 136 */ 137 int 138 lx_mlock(uintptr_t addr, uintptr_t len) 139 { 140 uintptr_t addr1 = addr & PAGEMASK; 141 uintptr_t len1 = len + (addr & PAGEOFFSET); 142 143 return (mlock((void *)addr1, (size_t)len1) ? -errno : 0); 144 } 145 146 int 147 lx_mlockall(uintptr_t flags) 148 { 149 return (mlockall(flags) ? -errno : 0); 150 } 151 152 int 153 lx_munlock(uintptr_t addr, uintptr_t len) 154 { 155 uintptr_t addr1 = addr & PAGEMASK; 156 uintptr_t len1 = len + (addr & PAGEOFFSET); 157 158 return (munlock((void *)addr1, (size_t)len1) ? -errno : 0); 159 } 160 161 int 162 lx_munlockall(void) 163 { 164 return (munlockall() ? -errno : 0); 165 } 166 167 int 168 lx_msync(uintptr_t addr, uintptr_t len, uintptr_t flags) 169 { 170 return (msync((void *)addr, (size_t)len, flags) ? -errno : 0); 171 } 172 173 /* 174 * Solaris recognizes more flags than Linux, so we don't want to inadvertently 175 * use what would be an invalid flag on Linux. Linux also allows the length to 176 * be zero, while Solaris does not. 177 */ 178 int 179 lx_madvise(uintptr_t start, uintptr_t len, uintptr_t advice) 180 { 181 if (len == 0) 182 return (0); 183 184 switch (advice) { 185 case MADV_NORMAL: 186 case MADV_RANDOM: 187 case MADV_SEQUENTIAL: 188 case MADV_WILLNEED: 189 case MADV_DONTNEED: 190 return (madvise((void *)start, len, advice) ? -errno : 0); 191 192 default: 193 return (-EINVAL); 194 } 195 } 196 197 /* 198 * mprotect() is identical except that we ignore the Linux flags PROT_GROWSDOWN 199 * and PROT_GROWSUP, which have no equivalent on Solaris. 200 */ 201 #define LX_PROT_GROWSDOWN 0x01000000 202 #define LX_PROT_GROWSUP 0x02000000 203 204 int 205 lx_mprotect(uintptr_t start, uintptr_t len, uintptr_t prot) 206 { 207 prot &= ~(LX_PROT_GROWSUP | LX_PROT_GROWSDOWN); 208 209 return (mprotect((void *)start, len, prot) ? -errno : 0); 210 }