1 /*- 2 * Copyright (c) 1999 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/systm.h> 35 #include <sys/sysctl.h> 36 #include <sys/proc.h> 37 #include <sys/malloc.h> 38 #include <sys/jail.h> 39 #include <sys/lock.h> 40 #include <sys/mutex.h> 41 42 #include "opt_compat.h" 43 44 #if !COMPAT_LINUX32 45 #include <machine/../linux/linux.h> 46 #else 47 #include <machine/../linux32/linux.h> 48 #endif 49 #include <compat/linux/linux_mib.h> 50 51 struct linux_prison { 52 char pr_osname[LINUX_MAX_UTSNAME]; 53 char pr_osrelease[LINUX_MAX_UTSNAME]; 54 int pr_oss_version; 55 }; 56 57 SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0, 58 "Linux mode"); 59 60 static struct mtx osname_lock; 61 MTX_SYSINIT(linux_osname, &osname_lock, "linux osname", MTX_DEF); 62 63 static char linux_osname[LINUX_MAX_UTSNAME] = "Linux"; 64 65 static int 66 linux_sysctl_osname(SYSCTL_HANDLER_ARGS) 67 { 68 char osname[LINUX_MAX_UTSNAME]; 69 int error; 70 71 linux_get_osname(req->td, osname); 72 error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req); 73 if (error || req->newptr == NULL) 74 return (error); 75 error = linux_set_osname(req->td, osname); 76 return (error); 77 } 78 79 SYSCTL_PROC(_compat_linux, OID_AUTO, osname, 80 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON, 81 0, 0, linux_sysctl_osname, "A", 82 "Linux kernel OS name"); 83 84 static char linux_osrelease[LINUX_MAX_UTSNAME] = "2.4.2"; 85 86 static int 87 linux_sysctl_osrelease(SYSCTL_HANDLER_ARGS) 88 { 89 char osrelease[LINUX_MAX_UTSNAME]; 90 int error; 91 92 linux_get_osrelease(req->td, osrelease); 93 error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req); 94 if (error || req->newptr == NULL) 95 return (error); 96 error = linux_set_osrelease(req->td, osrelease); 97 return (error); 98 } 99 100 SYSCTL_PROC(_compat_linux, OID_AUTO, osrelease, 101 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON, 102 0, 0, linux_sysctl_osrelease, "A", 103 "Linux kernel OS release"); 104 105 static int linux_oss_version = 0x030600; 106 107 static int 108 linux_sysctl_oss_version(SYSCTL_HANDLER_ARGS) 109 { 110 int oss_version; 111 int error; 112 113 oss_version = linux_get_oss_version(req->td); 114 error = sysctl_handle_int(oidp, &oss_version, 0, req); 115 if (error || req->newptr == NULL) 116 return (error); 117 error = linux_set_oss_version(req->td, oss_version); 118 return (error); 119 } 120 121 SYSCTL_PROC(_compat_linux, OID_AUTO, oss_version, 122 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON, 123 0, 0, linux_sysctl_oss_version, "I", 124 "Linux OSS version"); 125 126 /* 127 * Returns holding the prison mutex if return non-NULL. 128 */ 129 static struct prison * 130 linux_get_prison(struct thread *td) 131 { 132 register struct prison *pr; 133 register struct linux_prison *lpr; 134 135 KASSERT(td == curthread, ("linux_get_prison() called on !curthread")); 136 if (!jailed(td->td_ucred)) 137 return (NULL); 138 pr = td->td_ucred->cr_prison; 139 mtx_lock(&pr->pr_mtx); 140 if (pr->pr_linux == NULL) { 141 /* 142 * If we don't have a linux prison structure yet, allocate 143 * one. We have to handle the race where another thread 144 * could be adding a linux prison to this process already. 145 */ 146 mtx_unlock(&pr->pr_mtx); 147 lpr = malloc(sizeof(struct linux_prison), M_PRISON, 148 M_WAITOK | M_ZERO); 149 mtx_lock(&pr->pr_mtx); 150 if (pr->pr_linux == NULL) 151 pr->pr_linux = lpr; 152 else 153 free(lpr, M_PRISON); 154 } 155 return (pr); 156 } 157 158 void 159 linux_mib_destroy(void) 160 { 161 162 mtx_destroy(&osname_lock); 163 } 164 165 void 166 linux_get_osname(struct thread *td, char *dst) 167 { 168 register struct prison *pr; 169 register struct linux_prison *lpr; 170 171 pr = td->td_ucred->cr_prison; 172 if (pr != NULL) { 173 mtx_lock(&pr->pr_mtx); 174 if (pr->pr_linux != NULL) { 175 lpr = (struct linux_prison *)pr->pr_linux; 176 if (lpr->pr_osname[0]) { 177 bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME); 178 mtx_unlock(&pr->pr_mtx); 179 return; 180 } 181 } 182 mtx_unlock(&pr->pr_mtx); 183 } 184 185 mtx_lock(&osname_lock); 186 bcopy(linux_osname, dst, LINUX_MAX_UTSNAME); 187 mtx_unlock(&osname_lock); 188 } 189 190 int 191 linux_set_osname(struct thread *td, char *osname) 192 { 193 struct prison *pr; 194 struct linux_prison *lpr; 195 196 pr = linux_get_prison(td); 197 if (pr != NULL) { 198 lpr = (struct linux_prison *)pr->pr_linux; 199 strcpy(lpr->pr_osname, osname); 200 mtx_unlock(&pr->pr_mtx); 201 } else { 202 mtx_lock(&osname_lock); 203 strcpy(linux_osname, osname); 204 mtx_unlock(&osname_lock); 205 } 206 207 return (0); 208 } 209 210 void 211 linux_get_osrelease(struct thread *td, char *dst) 212 { 213 register struct prison *pr; 214 struct linux_prison *lpr; 215 216 pr = td->td_ucred->cr_prison; 217 if (pr != NULL) { 218 mtx_lock(&pr->pr_mtx); 219 if (pr->pr_linux != NULL) { 220 lpr = (struct linux_prison *)pr->pr_linux; 221 if (lpr->pr_osrelease[0]) { 222 bcopy(lpr->pr_osrelease, dst, 223 LINUX_MAX_UTSNAME); 224 mtx_unlock(&pr->pr_mtx); 225 return; 226 } 227 } 228 mtx_unlock(&pr->pr_mtx); 229 } 230 231 mtx_lock(&osname_lock); 232 bcopy(linux_osrelease, dst, LINUX_MAX_UTSNAME); 233 mtx_unlock(&osname_lock); 234 } 235 236 int 237 linux_set_osrelease(struct thread *td, char *osrelease) 238 { 239 struct prison *pr; 240 struct linux_prison *lpr; 241 242 pr = linux_get_prison(td); 243 if (pr != NULL) { 244 lpr = (struct linux_prison *)pr->pr_linux; 245 strcpy(lpr->pr_osrelease, osrelease); 246 mtx_unlock(&pr->pr_mtx); 247 } else { 248 mtx_lock(&osname_lock); 249 strcpy(linux_osrelease, osrelease); 250 mtx_unlock(&osname_lock); 251 } 252 253 return (0); 254 } 255 256 int 257 linux_get_oss_version(struct thread *td) 258 { 259 register struct prison *pr; 260 register struct linux_prison *lpr; 261 int version; 262 263 pr = td->td_ucred->cr_prison; 264 if (pr != NULL) { 265 mtx_lock(&pr->pr_mtx); 266 if (pr->pr_linux != NULL) { 267 lpr = (struct linux_prison *)pr->pr_linux; 268 if (lpr->pr_oss_version) { 269 version = lpr->pr_oss_version; 270 mtx_unlock(&pr->pr_mtx); 271 return (version); 272 } 273 } 274 mtx_unlock(&pr->pr_mtx); 275 } 276 277 mtx_lock(&osname_lock); 278 version = linux_oss_version; 279 mtx_unlock(&osname_lock); 280 return (version); 281 } 282 283 int 284 linux_set_oss_version(struct thread *td, int oss_version) 285 { 286 struct prison *pr; 287 struct linux_prison *lpr; 288 289 pr = linux_get_prison(td); 290 if (pr != NULL) { 291 lpr = (struct linux_prison *)pr->pr_linux; 292 lpr->pr_oss_version = oss_version; 293 mtx_unlock(&pr->pr_mtx); 294 } else { 295 mtx_lock(&osname_lock); 296 linux_oss_version = oss_version; 297 mtx_unlock(&osname_lock); 298 } 299 300 return (0); 301 } 302 303 #ifdef DEBUG 304 305 u_char linux_debug_map[howmany(LINUX_SYS_MAXSYSCALL, sizeof(u_char))]; 306 307 static int 308 linux_debug(int syscall, int toggle, int global) 309 { 310 311 if (global) { 312 char c = toggle ? 0 : 0xff; 313 314 memset(linux_debug_map, c, sizeof(linux_debug_map)); 315 return (0); 316 } 317 if (syscall < 0 || syscall >= LINUX_SYS_MAXSYSCALL) 318 return (EINVAL); 319 if (toggle) 320 clrbit(linux_debug_map, syscall); 321 else 322 setbit(linux_debug_map, syscall); 323 return (0); 324 } 325 326 /* 327 * Usage: sysctl linux.debug=<syscall_nr>.<0/1> 328 * 329 * E.g.: sysctl linux.debug=21.0 330 * 331 * As a special case, syscall "all" will apply to all syscalls globally. 332 */ 333 #define LINUX_MAX_DEBUGSTR 16 334 static int 335 linux_sysctl_debug(SYSCTL_HANDLER_ARGS) 336 { 337 char value[LINUX_MAX_DEBUGSTR], *p; 338 int error, sysc, toggle; 339 int global = 0; 340 341 value[0] = '\0'; 342 error = sysctl_handle_string(oidp, value, LINUX_MAX_DEBUGSTR, req); 343 if (error || req->newptr == NULL) 344 return (error); 345 for (p = value; *p != '\0' && *p != '.'; p++); 346 if (*p == '\0') 347 return (EINVAL); 348 *p++ = '\0'; 349 sysc = strtol(value, NULL, 0); 350 toggle = strtol(p, NULL, 0); 351 if (strcmp(value, "all") == 0) 352 global = 1; 353 error = linux_debug(sysc, toggle, global); 354 return (error); 355 } 356 357 SYSCTL_PROC(_compat_linux, OID_AUTO, debug, 358 CTLTYPE_STRING | CTLFLAG_RW, 359 0, 0, linux_sysctl_debug, "A", 360 "Linux debugging control"); 361 362 #endif /* DEBUG */ 363