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 <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) 41 struct brand_mach_ops native_mach_ops = { 42 NULL, NULL 43 }; 44 #else 45 struct brand_mach_ops native_mach_ops = { 46 NULL, NULL, NULL, NULL, NULL, NULL 47 }; 48 #endif 49 50 brand_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 */ 61 struct brand_list { 62 int bl_refcnt; 63 struct brand_list *bl_next; 64 brand_t *bl_brand; 65 }; 66 67 static struct brand_list *brand_list = NULL; 68 69 /* 70 * This lock protects the integrity of the brand list. 71 */ 72 static kmutex_t brand_list_lock; 73 74 void 75 brand_init() 76 { 77 mutex_init(&brand_list_lock, NULL, MUTEX_DEFAULT, NULL); 78 p0.p_brand = &native_brand; 79 } 80 81 int 82 brand_register(brand_t *brand) 83 { 84 struct brand_list *list, *scan; 85 86 if (brand == NULL) 87 return (EINVAL); 88 89 if (is_system_labeled()) { 90 cmn_err(CE_WARN, 91 "Branded zones are not allowed on labeled systems."); 92 return (EINVAL); 93 } 94 95 if (brand->b_version != SUPPORTED_BRAND_VERSION) { 96 if (brand->b_version < SUPPORTED_BRAND_VERSION) { 97 cmn_err(CE_WARN, 98 "brand '%s' was built to run on older versions " 99 "of Solaris.", 100 brand->b_name); 101 } else { 102 cmn_err(CE_WARN, 103 "brand '%s' was built to run on a newer version " 104 "of Solaris.", 105 brand->b_name); 106 } 107 return (EINVAL); 108 } 109 110 /* Sanity checks */ 111 if (brand->b_name == NULL || brand->b_ops == NULL || 112 brand->b_ops->b_brandsys == NULL) { 113 cmn_err(CE_WARN, "Malformed brand"); 114 return (EINVAL); 115 } 116 117 list = kmem_alloc(sizeof (struct brand_list), KM_SLEEP); 118 119 /* Add the brand to the list of loaded brands. */ 120 mutex_enter(&brand_list_lock); 121 122 /* 123 * Check to be sure we haven't already registered this brand. 124 */ 125 for (scan = brand_list; scan != NULL; scan = scan->bl_next) { 126 if (strcmp(brand->b_name, scan->bl_brand->b_name) == 0) { 127 cmn_err(CE_WARN, 128 "Invalid attempt to load a second instance of " 129 "brand %s", brand->b_name); 130 mutex_exit(&brand_list_lock); 131 kmem_free(list, sizeof (struct brand_list)); 132 return (EINVAL); 133 } 134 } 135 136 list->bl_brand = brand; 137 list->bl_refcnt = 0; 138 list->bl_next = brand_list; 139 brand_list = list; 140 mutex_exit(&brand_list_lock); 141 142 return (0); 143 } 144 145 /* 146 * The kernel module implementing this brand is being unloaded, so remove 147 * it from the list of active brands. 148 */ 149 int 150 brand_unregister(brand_t *brand) 151 { 152 struct brand_list *list, *prev; 153 154 /* Sanity checks */ 155 if (brand == NULL || brand->b_name == NULL) { 156 cmn_err(CE_WARN, "Malformed brand"); 157 return (EINVAL); 158 } 159 160 prev = NULL; 161 mutex_enter(&brand_list_lock); 162 163 for (list = brand_list; list != NULL; list = list->bl_next) { 164 if (list->bl_brand == brand) 165 break; 166 prev = list; 167 } 168 169 if (list == NULL) { 170 cmn_err(CE_WARN, "Brand %s wasn't registered", brand->b_name); 171 mutex_exit(&brand_list_lock); 172 return (EINVAL); 173 } 174 175 if (list->bl_refcnt > 0) { 176 cmn_err(CE_WARN, "Unregistering brand %s which is still in use", 177 brand->b_name); 178 mutex_exit(&brand_list_lock); 179 return (EBUSY); 180 } 181 182 /* Remove brand from the list */ 183 if (prev != NULL) 184 prev->bl_next = list->bl_next; 185 else 186 brand_list = list->bl_next; 187 188 mutex_exit(&brand_list_lock); 189 190 kmem_free(list, sizeof (struct brand_list)); 191 192 return (0); 193 } 194 195 /* 196 * Record that a zone of this brand has been instantiated. If the kernel 197 * module implementing this brand's functionality is not present, this 198 * routine attempts to load the module as a side effect. 199 */ 200 brand_t * 201 brand_register_zone(struct brand_attr *attr) 202 { 203 struct brand_list *l = NULL; 204 ddi_modhandle_t hdl = NULL; 205 char *modname; 206 int err = 0; 207 208 if (is_system_labeled()) { 209 cmn_err(CE_WARN, 210 "Branded zones are not allowed on labeled systems."); 211 return (NULL); 212 } 213 214 /* 215 * We make at most two passes through this loop. The first time 216 * through, we're looking to see if this is a new user of an 217 * already loaded brand. If the brand hasn't been loaded, we 218 * call ddi_modopen() to force it to be loaded and then make a 219 * second pass through the list of brands. If we don't find the 220 * brand the second time through it means that the modname 221 * specified in the brand_attr structure doesn't provide the brand 222 * specified in the brandname field. This would suggest a bug in 223 * the brand's config.xml file. We close the module and return 224 * 'NULL' to the caller. 225 */ 226 for (;;) { 227 /* 228 * Search list of loaded brands 229 */ 230 mutex_enter(&brand_list_lock); 231 for (l = brand_list; l != NULL; l = l->bl_next) 232 if (strcmp(attr->ba_brandname, 233 l->bl_brand->b_name) == 0) 234 break; 235 if ((l != NULL) || (hdl != NULL)) 236 break; 237 mutex_exit(&brand_list_lock); 238 239 /* 240 * We didn't find that the requested brand has been loaded 241 * yet, so we trigger the load of the appropriate kernel 242 * module and search the list again. 243 */ 244 modname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 245 (void) strcpy(modname, "brand/"); 246 (void) strcat(modname, attr->ba_modname); 247 hdl = ddi_modopen(modname, KRTLD_MODE_FIRST, &err); 248 kmem_free(modname, MAXPATHLEN); 249 250 if (err != 0) 251 return (NULL); 252 } 253 254 /* 255 * If we found the matching brand, bump its reference count. 256 */ 257 if (l != NULL) 258 l->bl_refcnt++; 259 260 mutex_exit(&brand_list_lock); 261 262 if (hdl != NULL) 263 (void) ddi_modclose(hdl); 264 265 return ((l != NULL) ? l->bl_brand : NULL); 266 } 267 268 /* 269 * Return the number of zones currently using this brand. 270 */ 271 int 272 brand_zone_count(struct brand *bp) 273 { 274 struct brand_list *l; 275 int cnt = 0; 276 277 mutex_enter(&brand_list_lock); 278 for (l = brand_list; l != NULL; l = l->bl_next) 279 if (l->bl_brand == bp) { 280 cnt = l->bl_refcnt; 281 break; 282 } 283 mutex_exit(&brand_list_lock); 284 285 return (cnt); 286 } 287 288 void 289 brand_unregister_zone(struct brand *bp) 290 { 291 struct brand_list *list; 292 293 mutex_enter(&brand_list_lock); 294 for (list = brand_list; list != NULL; list = list->bl_next) { 295 if (list->bl_brand == bp) { 296 ASSERT(list->bl_refcnt > 0); 297 list->bl_refcnt--; 298 break; 299 } 300 } 301 mutex_exit(&brand_list_lock); 302 } 303 304 void 305 brand_setbrand(proc_t *p) 306 { 307 brand_t *bp = p->p_zone->zone_brand; 308 309 ASSERT(bp != NULL); 310 ASSERT(p->p_brand == &native_brand); 311 312 /* 313 * We should only be called from exec(), when we know the process 314 * is single-threaded. 315 */ 316 ASSERT(p->p_tlist == p->p_tlist->t_forw); 317 318 p->p_brand = bp; 319 if (PROC_IS_BRANDED(p)) { 320 BROP(p)->b_setbrand(p); 321 lwp_attach_brand_hdlrs(p->p_tlist->t_lwp); 322 } 323 } 324