summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/brand.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/brand.c')
-rw-r--r--usr/src/uts/common/os/brand.c323
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);
+ }
+}