brand.c revision 4141:ddd21f3d4066
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 2007 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 <sys/kmem.h> 29#include <sys/errno.h> 30#include <sys/systm.h> 31#include <sys/cmn_err.h> 32#include <sys/brand.h> 33#include <sys/machbrand.h> 34#include <sys/modctl.h> 35#include <sys/rwlock.h> 36#include <sys/zone.h> 37 38#define SUPPORTED_BRAND_VERSION BRAND_VER_1 39 40#if defined(__sparcv9) 41struct brand_mach_ops native_mach_ops = { 42 NULL, NULL 43}; 44#else /* !__sparcv9 */ 45struct brand_mach_ops native_mach_ops = { 46 NULL, NULL, NULL, NULL, NULL, NULL 47}; 48#endif /* !__sparcv9 */ 49 50brand_t native_brand = { 51 BRAND_VER_1, 52 "native", 53 NULL, 54 &native_mach_ops 55}; 56 57/* 58 * Used to maintain a list of all the brands currently loaded into the 59 * kernel. 60 */ 61struct brand_list { 62 int bl_refcnt; 63 struct brand_list *bl_next; 64 brand_t *bl_brand; 65}; 66 67static struct brand_list *brand_list = NULL; 68 69/* 70 * Used to enable brand platform specific interposition code 71 */ 72#pragma weak brand_plat_interposition_init 73extern void brand_plat_interposition_init(void); 74 75/* 76 * This lock protects the integrity of the brand list. 77 */ 78static kmutex_t brand_list_lock; 79 80void 81brand_init() 82{ 83 brand_plat_interposition_init(); 84 mutex_init(&brand_list_lock, NULL, MUTEX_DEFAULT, NULL); 85 p0.p_brand = &native_brand; 86} 87 88int 89brand_register(brand_t *brand) 90{ 91 struct brand_list *list, *scan; 92 93 if (brand == NULL) 94 return (EINVAL); 95 96 if (is_system_labeled()) { 97 cmn_err(CE_WARN, 98 "Branded zones are not allowed on labeled systems."); 99 return (EINVAL); 100 } 101 102 if (brand->b_version != SUPPORTED_BRAND_VERSION) { 103 if (brand->b_version < SUPPORTED_BRAND_VERSION) { 104 cmn_err(CE_WARN, 105 "brand '%s' was built to run on older versions " 106 "of Solaris.", 107 brand->b_name); 108 } else { 109 cmn_err(CE_WARN, 110 "brand '%s' was built to run on a newer version " 111 "of Solaris.", 112 brand->b_name); 113 } 114 return (EINVAL); 115 } 116 117 /* Sanity checks */ 118 if (brand->b_name == NULL || brand->b_ops == NULL || 119 brand->b_ops->b_brandsys == NULL) { 120 cmn_err(CE_WARN, "Malformed brand"); 121 return (EINVAL); 122 } 123 124 list = kmem_alloc(sizeof (struct brand_list), KM_SLEEP); 125 126 /* Add the brand to the list of loaded brands. */ 127 mutex_enter(&brand_list_lock); 128 129 /* 130 * Check to be sure we haven't already registered this brand. 131 */ 132 for (scan = brand_list; scan != NULL; scan = scan->bl_next) { 133 if (strcmp(brand->b_name, scan->bl_brand->b_name) == 0) { 134 cmn_err(CE_WARN, 135 "Invalid attempt to load a second instance of " 136 "brand %s", brand->b_name); 137 mutex_exit(&brand_list_lock); 138 kmem_free(list, sizeof (struct brand_list)); 139 return (EINVAL); 140 } 141 } 142 143 list->bl_brand = brand; 144 list->bl_refcnt = 0; 145 list->bl_next = brand_list; 146 brand_list = list; 147 mutex_exit(&brand_list_lock); 148 149 return (0); 150} 151 152/* 153 * The kernel module implementing this brand is being unloaded, so remove 154 * it from the list of active brands. 155 */ 156int 157brand_unregister(brand_t *brand) 158{ 159 struct brand_list *list, *prev; 160 161 /* Sanity checks */ 162 if (brand == NULL || brand->b_name == NULL) { 163 cmn_err(CE_WARN, "Malformed brand"); 164 return (EINVAL); 165 } 166 167 prev = NULL; 168 mutex_enter(&brand_list_lock); 169 170 for (list = brand_list; list != NULL; list = list->bl_next) { 171 if (list->bl_brand == brand) 172 break; 173 prev = list; 174 } 175 176 if (list == NULL) { 177 cmn_err(CE_WARN, "Brand %s wasn't registered", brand->b_name); 178 mutex_exit(&brand_list_lock); 179 return (EINVAL); 180 } 181 182 if (list->bl_refcnt > 0) { 183 cmn_err(CE_WARN, "Unregistering brand %s which is still in use", 184 brand->b_name); 185 mutex_exit(&brand_list_lock); 186 return (EBUSY); 187 } 188 189 /* Remove brand from the list */ 190 if (prev != NULL) 191 prev->bl_next = list->bl_next; 192 else 193 brand_list = list->bl_next; 194 195 mutex_exit(&brand_list_lock); 196 197 kmem_free(list, sizeof (struct brand_list)); 198 199 return (0); 200} 201 202/* 203 * Record that a zone of this brand has been instantiated. If the kernel 204 * module implementing this brand's functionality is not present, this 205 * routine attempts to load the module as a side effect. 206 */ 207brand_t * 208brand_register_zone(struct brand_attr *attr) 209{ 210 struct brand_list *l = NULL; 211 ddi_modhandle_t hdl = NULL; 212 char *modname; 213 int err = 0; 214 215 if (is_system_labeled()) { 216 cmn_err(CE_WARN, 217 "Branded zones are not allowed on labeled systems."); 218 return (NULL); 219 } 220 221 /* 222 * We make at most two passes through this loop. The first time 223 * through, we're looking to see if this is a new user of an 224 * already loaded brand. If the brand hasn't been loaded, we 225 * call ddi_modopen() to force it to be loaded and then make a 226 * second pass through the list of brands. If we don't find the 227 * brand the second time through it means that the modname 228 * specified in the brand_attr structure doesn't provide the brand 229 * specified in the brandname field. This would suggest a bug in 230 * the brand's config.xml file. We close the module and return 231 * 'NULL' to the caller. 232 */ 233 for (;;) { 234 /* 235 * Search list of loaded brands 236 */ 237 mutex_enter(&brand_list_lock); 238 for (l = brand_list; l != NULL; l = l->bl_next) 239 if (strcmp(attr->ba_brandname, 240 l->bl_brand->b_name) == 0) 241 break; 242 if ((l != NULL) || (hdl != NULL)) 243 break; 244 mutex_exit(&brand_list_lock); 245 246 /* 247 * We didn't find that the requested brand has been loaded 248 * yet, so we trigger the load of the appropriate kernel 249 * module and search the list again. 250 */ 251 modname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 252 (void) strcpy(modname, "brand/"); 253 (void) strcat(modname, attr->ba_modname); 254 hdl = ddi_modopen(modname, KRTLD_MODE_FIRST, &err); 255 kmem_free(modname, MAXPATHLEN); 256 257 if (err != 0) 258 return (NULL); 259 } 260 261 /* 262 * If we found the matching brand, bump its reference count. 263 */ 264 if (l != NULL) 265 l->bl_refcnt++; 266 267 mutex_exit(&brand_list_lock); 268 269 if (hdl != NULL) 270 (void) ddi_modclose(hdl); 271 272 return ((l != NULL) ? l->bl_brand : NULL); 273} 274 275/* 276 * Return the number of zones currently using this brand. 277 */ 278int 279brand_zone_count(struct brand *bp) 280{ 281 struct brand_list *l; 282 int cnt = 0; 283 284 mutex_enter(&brand_list_lock); 285 for (l = brand_list; l != NULL; l = l->bl_next) 286 if (l->bl_brand == bp) { 287 cnt = l->bl_refcnt; 288 break; 289 } 290 mutex_exit(&brand_list_lock); 291 292 return (cnt); 293} 294 295void 296brand_unregister_zone(struct brand *bp) 297{ 298 struct brand_list *list; 299 300 mutex_enter(&brand_list_lock); 301 for (list = brand_list; list != NULL; list = list->bl_next) { 302 if (list->bl_brand == bp) { 303 ASSERT(list->bl_refcnt > 0); 304 list->bl_refcnt--; 305 break; 306 } 307 } 308 mutex_exit(&brand_list_lock); 309} 310 311void 312brand_setbrand(proc_t *p) 313{ 314 brand_t *bp = p->p_zone->zone_brand; 315 316 ASSERT(bp != NULL); 317 ASSERT(p->p_brand == &native_brand); 318 319 /* 320 * We should only be called from exec(), when we know the process 321 * is single-threaded. 322 */ 323 ASSERT(p->p_tlist == p->p_tlist->t_forw); 324 325 p->p_brand = bp; 326 if (PROC_IS_BRANDED(p)) { 327 BROP(p)->b_setbrand(p); 328 lwp_attach_brand_hdlrs(p->p_tlist->t_lwp); 329 } 330} 331 332#if defined(__sparcv9) 333/* 334 * Currently, only sparc has platform level brand syscall interposition. 335 * On x86 we're able to enable syscall interposition on a per-cpu basis 336 * when a branded thread is scheduled to run on a cpu. 337 */ 338 339/* Local variables needed for dynamic syscall interposition support */ 340static kmutex_t brand_interposition_lock; 341static int brand_interposition_count; 342static uint32_t syscall_trap_patch_instr_orig; 343static uint32_t syscall_trap32_patch_instr_orig; 344 345/* Trap Table syscall entry hot patch points */ 346extern void syscall_trap_patch_point(void); 347extern void syscall_trap32_patch_point(void); 348 349/* Alternate syscall entry handlers used when branded zones are running */ 350extern void syscall_wrapper(void); 351extern void syscall_wrapper32(void); 352 353/* Macros used to facilitate sparcv9 instruction generation */ 354#define BA_A_INSTR 0x30800000 /* ba,a addr */ 355#define DISP22(from, to) \ 356 ((((uintptr_t)(to) - (uintptr_t)(from)) >> 2) & 0x3fffff) 357 358void 359brand_plat_interposition_init(void) 360{ 361 mutex_init(&brand_interposition_lock, NULL, MUTEX_DEFAULT, NULL); 362 brand_interposition_count = 0; 363} 364 365/*ARGSUSED*/ 366void 367brand_plat_interposition_enable(brand_t *bp) 368{ 369 ASSERT((bp != NULL) && (bp != &native_brand)); 370 371 mutex_enter(&brand_interposition_lock); 372 ASSERT(brand_interposition_count >= 0); 373 374 if (brand_interposition_count++ > 0) { 375 mutex_exit(&brand_interposition_lock); 376 return; 377 } 378 379 /* 380 * This is the first branded zone that is being enabled on 381 * this system. 382 * 383 * Before we hot patch the kernel save the current instructions 384 * so that we can restore them if all branded zones on the 385 * system are shutdown. 386 */ 387 syscall_trap_patch_instr_orig = 388 *(uint32_t *)syscall_trap_patch_point; 389 syscall_trap32_patch_instr_orig = 390 *(uint32_t *)syscall_trap32_patch_point; 391 392 /* 393 * Modify the trap table at the patch points. 394 * 395 * We basically replace the first instruction at the patch 396 * point with a ba,a instruction that will transfer control 397 * to syscall_wrapper or syscall_wrapper32 for 64-bit and 398 * 32-bit syscalls respectively. It's important to note that 399 * the annul bit is set in the branch so we don't execute 400 * the instruction directly following the one we're patching 401 * during the branch's delay slot. 402 * 403 * It also doesn't matter that we're not atomically updating both 404 * the 64 and 32 bit syscall paths at the same time since there's 405 * no actual branded processes running on the system yet. 406 */ 407 hot_patch_kernel_text((caddr_t)syscall_trap_patch_point, 408 BA_A_INSTR | DISP22(syscall_trap_patch_point, syscall_wrapper), 409 4); 410 hot_patch_kernel_text((caddr_t)syscall_trap32_patch_point, 411 BA_A_INSTR | DISP22(syscall_trap32_patch_point, syscall_wrapper32), 412 4); 413 414 mutex_exit(&brand_interposition_lock); 415} 416 417/*ARGSUSED*/ 418void 419brand_plat_interposition_disable(brand_t *bp) 420{ 421 ASSERT((bp != NULL) && (bp != &native_brand)); 422 423 mutex_enter(&brand_interposition_lock); 424 ASSERT(brand_interposition_count > 0); 425 426 if (--brand_interposition_count > 0) { 427 mutex_exit(&brand_interposition_lock); 428 return; 429 } 430 431 /* 432 * The last branded zone on this system has been shutdown. 433 * 434 * Restore the original instructions at the trap table syscall 435 * patch points to disable the brand syscall interposition 436 * mechanism. 437 */ 438 hot_patch_kernel_text((caddr_t)syscall_trap_patch_point, 439 syscall_trap_patch_instr_orig, 4); 440 hot_patch_kernel_text((caddr_t)syscall_trap32_patch_point, 441 syscall_trap32_patch_instr_orig, 4); 442 443 mutex_exit(&brand_interposition_lock); 444} 445#endif /* __sparcv9 */ 446