diff options
Diffstat (limited to 'usr/src/uts/common/os/brand.c')
-rw-r--r-- | usr/src/uts/common/os/brand.c | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/usr/src/uts/common/os/brand.c b/usr/src/uts/common/os/brand.c new file mode 100644 index 0000000000..15d82871bf --- /dev/null +++ b/usr/src/uts/common/os/brand.c @@ -0,0 +1,323 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/kmem.h> +#include <sys/errno.h> +#include <sys/systm.h> +#include <sys/cmn_err.h> +#include <sys/brand.h> +#include <sys/machbrand.h> +#include <sys/modctl.h> +#include <sys/rwlock.h> +#include <sys/zone.h> + +#define SUPPORTED_BRAND_VERSION BRAND_VER_1 + +#if defined(__sparcv9) +struct brand_mach_ops native_mach_ops = { + NULL, NULL +}; +#else +struct brand_mach_ops native_mach_ops = { + NULL, NULL, NULL, NULL, NULL, NULL +}; +#endif + +brand_t native_brand = { + BRAND_VER_1, + "native", + NULL, + &native_mach_ops +}; + +/* + * Used to maintain a list of all the brands currently loaded into the + * kernel. + */ +struct brand_list { + int bl_refcnt; + struct brand_list *bl_next; + brand_t *bl_brand; +}; + +static struct brand_list *brand_list = NULL; + +/* + * This lock protects the integrity of the brand list. + */ +static kmutex_t brand_list_lock; + +void +brand_init() +{ + mutex_init(&brand_list_lock, NULL, MUTEX_DEFAULT, NULL); + p0.p_brand = &native_brand; +} + +int +brand_register(brand_t *brand) +{ + struct brand_list *list, *scan; + + if (brand == NULL) + return (EINVAL); + + if (is_system_labeled()) { + cmn_err(CE_WARN, + "Branded zones are not allowed on labeled systems."); + return (EINVAL); + } + + if (brand->b_version != SUPPORTED_BRAND_VERSION) { + if (brand->b_version < SUPPORTED_BRAND_VERSION) { + cmn_err(CE_WARN, + "brand '%s' was built to run on older versions " + "of Solaris.", + brand->b_name); + } else { + cmn_err(CE_WARN, + "brand '%s' was built to run on a newer version " + "of Solaris.", + brand->b_name); + } + return (EINVAL); + } + + /* Sanity checks */ + if (brand->b_name == NULL || brand->b_ops == NULL || + brand->b_ops->b_brandsys == NULL) { + cmn_err(CE_WARN, "Malformed brand"); + return (EINVAL); + } + + list = kmem_alloc(sizeof (struct brand_list), KM_SLEEP); + + /* Add the brand to the list of loaded brands. */ + mutex_enter(&brand_list_lock); + + /* + * Check to be sure we haven't already registered this brand. + */ + for (scan = brand_list; scan != NULL; scan = scan->bl_next) { + if (strcmp(brand->b_name, scan->bl_brand->b_name) == 0) { + cmn_err(CE_WARN, + "Invalid attempt to load a second instance of " + "brand %s", brand->b_name); + mutex_exit(&brand_list_lock); + kmem_free(list, sizeof (struct brand_list)); + return (EINVAL); + } + } + + list->bl_brand = brand; + list->bl_refcnt = 0; + list->bl_next = brand_list; + brand_list = list; + mutex_exit(&brand_list_lock); + + return (0); +} + +/* + * The kernel module implementing this brand is being unloaded, so remove + * it from the list of active brands. + */ +int +brand_unregister(brand_t *brand) +{ + struct brand_list *list, *prev; + + /* Sanity checks */ + if (brand == NULL || brand->b_name == NULL) { + cmn_err(CE_WARN, "Malformed brand"); + return (EINVAL); + } + + prev = NULL; + mutex_enter(&brand_list_lock); + + for (list = brand_list; list != NULL; list = list->bl_next) { + if (list->bl_brand == brand) + break; + prev = list; + } + + if (list == NULL) { + cmn_err(CE_WARN, "Brand %s wasn't registered", brand->b_name); + mutex_exit(&brand_list_lock); + return (EINVAL); + } + + if (list->bl_refcnt > 0) { + cmn_err(CE_WARN, "Unregistering brand %s which is still in use", + brand->b_name); + mutex_exit(&brand_list_lock); + return (EBUSY); + } + + /* Remove brand from the list */ + if (prev != NULL) + prev->bl_next = list->bl_next; + else + brand_list = list->bl_next; + + mutex_exit(&brand_list_lock); + + kmem_free(list, sizeof (struct brand_list)); + + return (0); +} + +/* + * Record that a zone of this brand has been instantiated. If the kernel + * module implementing this brand's functionality is not present, this + * routine attempts to load the module as a side effect. + */ +brand_t * +brand_register_zone(struct brand_attr *attr) +{ + struct brand_list *l = NULL; + ddi_modhandle_t hdl = NULL; + char *modname; + int err = 0; + + if (is_system_labeled()) { + cmn_err(CE_WARN, + "Branded zones are not allowed on labeled systems."); + return (NULL); + } + + /* + * We make at most two passes through this loop. The first time + * through, we're looking to see if this is a new user of an + * already loaded brand. If the brand hasn't been loaded, we + * call ddi_modopen() to force it to be loaded and then make a + * second pass through the list of brands. If we don't find the + * brand the second time through it means that the modname + * specified in the brand_attr structure doesn't provide the brand + * specified in the brandname field. This would suggest a bug in + * the brand's config.xml file. We close the module and return + * 'NULL' to the caller. + */ + for (;;) { + /* + * Search list of loaded brands + */ + mutex_enter(&brand_list_lock); + for (l = brand_list; l != NULL; l = l->bl_next) + if (strcmp(attr->ba_brandname, + l->bl_brand->b_name) == 0) + break; + if ((l != NULL) || (hdl != NULL)) + break; + mutex_exit(&brand_list_lock); + + /* + * We didn't find that the requested brand has been loaded + * yet, so we trigger the load of the appropriate kernel + * module and search the list again. + */ + modname = kmem_alloc(MAXPATHLEN, KM_SLEEP); + (void) strcpy(modname, "brand/"); + (void) strcat(modname, attr->ba_modname); + hdl = ddi_modopen(modname, KRTLD_MODE_FIRST, &err); + kmem_free(modname, MAXPATHLEN); + + if (err != 0) + return (NULL); + } + + /* + * If we found the matching brand, bump its reference count. + */ + if (l != NULL) + l->bl_refcnt++; + + mutex_exit(&brand_list_lock); + + if (hdl != NULL) + (void) ddi_modclose(hdl); + + return ((l != NULL) ? l->bl_brand : NULL); +} + +/* + * Return the number of zones currently using this brand. + */ +int +brand_zone_count(struct brand *bp) +{ + struct brand_list *l; + int cnt = 0; + + mutex_enter(&brand_list_lock); + for (l = brand_list; l != NULL; l = l->bl_next) + if (l->bl_brand == bp) { + cnt = l->bl_refcnt; + break; + } + mutex_exit(&brand_list_lock); + + return (cnt); +} + +void +brand_unregister_zone(struct brand *bp) +{ + struct brand_list *list; + + mutex_enter(&brand_list_lock); + for (list = brand_list; list != NULL; list = list->bl_next) { + if (list->bl_brand == bp) { + ASSERT(list->bl_refcnt > 0); + list->bl_refcnt--; + break; + } + } + mutex_exit(&brand_list_lock); +} + +void +brand_setbrand(proc_t *p) +{ + brand_t *bp = p->p_zone->zone_brand; + + ASSERT(bp != NULL); + ASSERT(p->p_brand == &native_brand); + + /* + * We should only be called from exec(), when we know the process + * is single-threaded. + */ + ASSERT(p->p_tlist == p->p_tlist->t_forw); + + p->p_brand = bp; + if (PROC_IS_BRANDED(p)) { + BROP(p)->b_setbrand(p); + lwp_attach_brand_hdlrs(p->p_tlist->t_lwp); + } +} |