xref: /illumos-gate/usr/src/uts/common/os/brand.c (revision fb46ffca)
19acbbeafSnn /*
29acbbeafSnn  * CDDL HEADER START
39acbbeafSnn  *
49acbbeafSnn  * The contents of this file are subject to the terms of the
59acbbeafSnn  * Common Development and Distribution License (the "License").
69acbbeafSnn  * You may not use this file except in compliance with the License.
79acbbeafSnn  *
89acbbeafSnn  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99acbbeafSnn  * or http://www.opensolaris.org/os/licensing.
109acbbeafSnn  * See the License for the specific language governing permissions
119acbbeafSnn  * and limitations under the License.
129acbbeafSnn  *
139acbbeafSnn  * When distributing Covered Code, include this CDDL HEADER in each
149acbbeafSnn  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159acbbeafSnn  * If applicable, add the following below this CDDL HEADER, with the
169acbbeafSnn  * fields enclosed by brackets "[]" replaced with your own identifying
179acbbeafSnn  * information: Portions Copyright [yyyy] [name of copyright owner]
189acbbeafSnn  *
199acbbeafSnn  * CDDL HEADER END
209acbbeafSnn  */
219acbbeafSnn /*
2280e2ca85S  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
239acbbeafSnn  */
259acbbeafSnn #include <sys/kmem.h>
269acbbeafSnn #include <sys/errno.h>
279acbbeafSnn #include <sys/systm.h>
289acbbeafSnn #include <sys/cmn_err.h>
299acbbeafSnn #include <sys/brand.h>
309acbbeafSnn #include <sys/machbrand.h>
319acbbeafSnn #include <sys/modctl.h>
329acbbeafSnn #include <sys/rwlock.h>
339acbbeafSnn #include <sys/zone.h>
3480e2ca85S #include <sys/pathname.h>
389acbbeafSnn #if defined(__sparcv9)
39725deb8fSedp /* sparcv9 uses system wide brand interposition hooks */
40725deb8fSedp static void brand_plat_interposition_enable(void);
41725deb8fSedp static void brand_plat_interposition_disable(void);
439acbbeafSnn struct brand_mach_ops native_mach_ops  = {
449acbbeafSnn 		NULL, NULL
459acbbeafSnn };
4659f2ff5cSedp #else /* !__sparcv9 */
479acbbeafSnn struct brand_mach_ops native_mach_ops  = {
48eb5a5c78SSurya Prakki 		NULL, NULL, NULL, NULL
499acbbeafSnn };
5059f2ff5cSedp #endif /* !__sparcv9 */
529acbbeafSnn brand_t native_brand = {
539acbbeafSnn 		BRAND_VER_1,
549acbbeafSnn 		"native",
559acbbeafSnn 		NULL,
569acbbeafSnn 		&native_mach_ops
579acbbeafSnn };
599acbbeafSnn /*
609acbbeafSnn  * Used to maintain a list of all the brands currently loaded into the
619acbbeafSnn  * kernel.
629acbbeafSnn  */
639acbbeafSnn struct brand_list {
649acbbeafSnn 	int			bl_refcnt;
659acbbeafSnn 	struct brand_list	*bl_next;
669acbbeafSnn 	brand_t			*bl_brand;
679acbbeafSnn };
699acbbeafSnn static struct brand_list *brand_list = NULL;
719acbbeafSnn /*
729acbbeafSnn  * This lock protects the integrity of the brand list.
739acbbeafSnn  */
749acbbeafSnn static kmutex_t brand_list_lock;
769acbbeafSnn void
brand_init()779acbbeafSnn brand_init()
789acbbeafSnn {
799acbbeafSnn 	mutex_init(&brand_list_lock, NULL, MUTEX_DEFAULT, NULL);
809acbbeafSnn 	p0.p_brand = &native_brand;
819acbbeafSnn }
839acbbeafSnn int
brand_register(brand_t * brand)849acbbeafSnn brand_register(brand_t *brand)
859acbbeafSnn {
869acbbeafSnn 	struct brand_list *list, *scan;
889acbbeafSnn 	if (brand == NULL)
899acbbeafSnn 		return (EINVAL);
919acbbeafSnn 	if (brand->b_version != SUPPORTED_BRAND_VERSION) {
929acbbeafSnn 		if (brand->b_version < SUPPORTED_BRAND_VERSION) {
939acbbeafSnn 			cmn_err(CE_WARN,
949acbbeafSnn 			    "brand '%s' was built to run on older versions "
959acbbeafSnn 			    "of Solaris.",
969acbbeafSnn 			    brand->b_name);
979acbbeafSnn 		} else {
989acbbeafSnn 			cmn_err(CE_WARN,
999acbbeafSnn 			    "brand '%s' was built to run on a newer version "
1009acbbeafSnn 			    "of Solaris.",
1019acbbeafSnn 			    brand->b_name);
1029acbbeafSnn 		}
1039acbbeafSnn 		return (EINVAL);
1049acbbeafSnn 	}
1069acbbeafSnn 	/* Sanity checks */
1079acbbeafSnn 	if (brand->b_name == NULL || brand->b_ops == NULL ||
1089acbbeafSnn 	    brand->b_ops->b_brandsys == NULL) {
1099acbbeafSnn 		cmn_err(CE_WARN, "Malformed brand");
1109acbbeafSnn 		return (EINVAL);
1119acbbeafSnn 	}
1139acbbeafSnn 	list = kmem_alloc(sizeof (struct brand_list), KM_SLEEP);
1159acbbeafSnn 	/* Add the brand to the list of loaded brands. */
1169acbbeafSnn 	mutex_enter(&brand_list_lock);
1189acbbeafSnn 	/*
1199acbbeafSnn 	 * Check to be sure we haven't already registered this brand.
1209acbbeafSnn 	 */
1219acbbeafSnn 	for (scan = brand_list; scan != NULL; scan = scan->bl_next) {
1229acbbeafSnn 		if (strcmp(brand->b_name, scan->bl_brand->b_name) == 0) {
1239acbbeafSnn 			cmn_err(CE_WARN,
1249acbbeafSnn 			    "Invalid attempt to load a second instance of "
1259acbbeafSnn 			    "brand %s", brand->b_name);
1269acbbeafSnn 			mutex_exit(&brand_list_lock);
1279acbbeafSnn 			kmem_free(list, sizeof (struct brand_list));
1289acbbeafSnn 			return (EINVAL);
1299acbbeafSnn 		}
1309acbbeafSnn 	}
132725deb8fSedp #if defined(__sparcv9)
133725deb8fSedp 	/* sparcv9 uses system wide brand interposition hooks */
134725deb8fSedp 	if (brand_list == NULL)
135725deb8fSedp 		brand_plat_interposition_enable();
136725deb8fSedp #endif /* __sparcv9 */
1389acbbeafSnn 	list->bl_brand = brand;
1399acbbeafSnn 	list->bl_refcnt = 0;
1409acbbeafSnn 	list->bl_next = brand_list;
1419acbbeafSnn 	brand_list = list;
1439acbbeafSnn 	mutex_exit(&brand_list_lock);
1459acbbeafSnn 	return (0);
1469acbbeafSnn }
1489acbbeafSnn /*
1499acbbeafSnn  * The kernel module implementing this brand is being unloaded, so remove
1509acbbeafSnn  * it from the list of active brands.
1519acbbeafSnn  */
1529acbbeafSnn int
brand_unregister(brand_t * brand)1539acbbeafSnn brand_unregister(brand_t *brand)
1549acbbeafSnn {
1559acbbeafSnn 	struct brand_list *list, *prev;
1579acbbeafSnn 	/* Sanity checks */
1589acbbeafSnn 	if (brand == NULL || brand->b_name == NULL) {
1599acbbeafSnn 		cmn_err(CE_WARN, "Malformed brand");
1609acbbeafSnn 		return (EINVAL);
1619acbbeafSnn 	}
1639acbbeafSnn 	prev = NULL;
1649acbbeafSnn 	mutex_enter(&brand_list_lock);
1669acbbeafSnn 	for (list = brand_list; list != NULL; list = list->bl_next) {
1679acbbeafSnn 		if (list->bl_brand == brand)
1689acbbeafSnn 			break;
1699acbbeafSnn 		prev = list;
1709acbbeafSnn 	}
1729acbbeafSnn 	if (list == NULL) {
1739acbbeafSnn 		cmn_err(CE_WARN, "Brand %s wasn't registered", brand->b_name);
1749acbbeafSnn 		mutex_exit(&brand_list_lock);
1759acbbeafSnn 		return (EINVAL);
1769acbbeafSnn 	}
1789acbbeafSnn 	if (list->bl_refcnt > 0) {
1799acbbeafSnn 		cmn_err(CE_WARN, "Unregistering brand %s which is still in use",
1809acbbeafSnn 		    brand->b_name);
1819acbbeafSnn 		mutex_exit(&brand_list_lock);
1829acbbeafSnn 		return (EBUSY);
1839acbbeafSnn 	}
1859acbbeafSnn 	/* Remove brand from the list */
1869acbbeafSnn 	if (prev != NULL)
1879acbbeafSnn 		prev->bl_next = list->bl_next;
1889acbbeafSnn 	else
1899acbbeafSnn 		brand_list = list->bl_next;
191725deb8fSedp #if defined(__sparcv9)
192725deb8fSedp 	/* sparcv9 uses system wide brand interposition hooks */
193725deb8fSedp 	if (brand_list == NULL)
194725deb8fSedp 		brand_plat_interposition_disable();
195725deb8fSedp #endif /* __sparcv9 */
1979acbbeafSnn 	mutex_exit(&brand_list_lock);
1999acbbeafSnn 	kmem_free(list, sizeof (struct brand_list));
2019acbbeafSnn 	return (0);
2029acbbeafSnn }
2049acbbeafSnn /*
2059acbbeafSnn  * Record that a zone of this brand has been instantiated.  If the kernel
2069acbbeafSnn  * module implementing this brand's functionality is not present, this
2079acbbeafSnn  * routine attempts to load the module as a side effect.
2089acbbeafSnn  */
2099acbbeafSnn brand_t *
brand_register_zone(struct brand_attr * attr)2109acbbeafSnn brand_register_zone(struct brand_attr *attr)
2119acbbeafSnn {
2129acbbeafSnn 	struct brand_list *l = NULL;
2139acbbeafSnn 	ddi_modhandle_t	hdl = NULL;
2149acbbeafSnn 	char *modname;
2159acbbeafSnn 	int err = 0;
2179acbbeafSnn 	if (is_system_labeled()) {
2189acbbeafSnn 		cmn_err(CE_WARN,
2199acbbeafSnn 		    "Branded zones are not allowed on labeled systems.");
2209acbbeafSnn 		return (NULL);
2219acbbeafSnn 	}
2239acbbeafSnn 	/*
2249acbbeafSnn 	 * We make at most two passes through this loop.  The first time
2259acbbeafSnn 	 * through, we're looking to see if this is a new user of an
2269acbbeafSnn 	 * already loaded brand.  If the brand hasn't been loaded, we
2279acbbeafSnn 	 * call ddi_modopen() to force it to be loaded and then make a
2289acbbeafSnn 	 * second pass through the list of brands.  If we don't find the
2299acbbeafSnn 	 * brand the second time through it means that the modname
2309acbbeafSnn 	 * specified in the brand_attr structure doesn't provide the brand
2319acbbeafSnn 	 * specified in the brandname field.  This would suggest a bug in
2329acbbeafSnn 	 * the brand's config.xml file.  We close the module and return
2339acbbeafSnn 	 * 'NULL' to the caller.
2349acbbeafSnn 	 */
2359acbbeafSnn 	for (;;) {
2369acbbeafSnn 		/*
2379acbbeafSnn 		 * Search list of loaded brands
2389acbbeafSnn 		 */
2399acbbeafSnn 		mutex_enter(&brand_list_lock);
2409acbbeafSnn 		for (l = brand_list; l != NULL; l = l->bl_next)
2419acbbeafSnn 			if (strcmp(attr->ba_brandname,
2429acbbeafSnn 			    l->bl_brand->b_name) == 0)
2439acbbeafSnn 				break;
2449acbbeafSnn 		if ((l != NULL) || (hdl != NULL))
2459acbbeafSnn 			break;
2469acbbeafSnn 		mutex_exit(&brand_list_lock);
2489acbbeafSnn 		/*
2499acbbeafSnn 		 * We didn't find that the requested brand has been loaded
2509acbbeafSnn 		 * yet, so we trigger the load of the appropriate kernel
2519acbbeafSnn 		 * module and search the list again.
2529acbbeafSnn 		 */
2539acbbeafSnn 		modname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2549acbbeafSnn 		(void) strcpy(modname, "brand/");
2559acbbeafSnn 		(void) strcat(modname, attr->ba_modname);
2569acbbeafSnn 		hdl = ddi_modopen(modname, KRTLD_MODE_FIRST, &err);
2579acbbeafSnn 		kmem_free(modname, MAXPATHLEN);
2599acbbeafSnn 		if (err != 0)
2609acbbeafSnn 			return (NULL);
2619acbbeafSnn 	}
2639acbbeafSnn 	/*
2649acbbeafSnn 	 * If we found the matching brand, bump its reference count.
2659acbbeafSnn 	 */
2669acbbeafSnn 	if (l != NULL)
2679acbbeafSnn 		l->bl_refcnt++;
2699acbbeafSnn 	mutex_exit(&brand_list_lock);
2719acbbeafSnn 	if (hdl != NULL)
2729acbbeafSnn 		(void) ddi_modclose(hdl);
2749acbbeafSnn 	return ((l != NULL) ? l->bl_brand : NULL);
2759acbbeafSnn }
2779acbbeafSnn /*
2789acbbeafSnn  * Return the number of zones currently using this brand.
2799acbbeafSnn  */
2809acbbeafSnn int
brand_zone_count(struct brand * bp)2819acbbeafSnn brand_zone_count(struct brand *bp)
2829acbbeafSnn {
2839acbbeafSnn 	struct brand_list *l;
2849acbbeafSnn 	int cnt = 0;
2869acbbeafSnn 	mutex_enter(&brand_list_lock);
2879acbbeafSnn 	for (l = brand_list; l != NULL; l = l->bl_next)
2889acbbeafSnn 		if (l->bl_brand == bp) {
2899acbbeafSnn 			cnt = l->bl_refcnt;
2909acbbeafSnn 			break;
2919acbbeafSnn 		}
2929acbbeafSnn 	mutex_exit(&brand_list_lock);
2949acbbeafSnn 	return (cnt);
2959acbbeafSnn }
2979acbbeafSnn void
brand_unregister_zone(struct brand * bp)2989acbbeafSnn brand_unregister_zone(struct brand *bp)
2999acbbeafSnn {
3009acbbeafSnn 	struct brand_list *list;
3029acbbeafSnn 	mutex_enter(&brand_list_lock);
3039acbbeafSnn 	for (list = brand_list; list != NULL; list = list->bl_next) {
3049acbbeafSnn 		if (list->bl_brand == bp) {
3059acbbeafSnn 			ASSERT(list->bl_refcnt > 0);
3069acbbeafSnn 			list->bl_refcnt--;
3079acbbeafSnn 			break;
3089acbbeafSnn 		}
3099acbbeafSnn 	}
3109acbbeafSnn 	mutex_exit(&brand_list_lock);
3119acbbeafSnn }
3139acbbeafSnn void
brand_setbrand(proc_t * p)3149acbbeafSnn brand_setbrand(proc_t *p)
3159acbbeafSnn {
3169acbbeafSnn 	brand_t *bp = p->p_zone->zone_brand;
3189acbbeafSnn 	ASSERT(bp != NULL);
3199acbbeafSnn 	ASSERT(p->p_brand == &native_brand);
3219acbbeafSnn 	/*
3229acbbeafSnn 	 * We should only be called from exec(), when we know the process
3239acbbeafSnn 	 * is single-threaded.
3249acbbeafSnn 	 */
3259acbbeafSnn 	ASSERT(p->p_tlist == p->p_tlist->t_forw);
3279acbbeafSnn 	p->p_brand = bp;
328fd9e7635Sedp 	ASSERT(PROC_IS_BRANDED(p));
329fd9e7635Sedp 	BROP(p)->b_setbrand(p);
330fd9e7635Sedp }
332fd9e7635Sedp void
brand_clearbrand(proc_t * p,boolean_t no_lwps)333e9f7cbf0SVamsi Nagineni brand_clearbrand(proc_t *p, boolean_t no_lwps)
334fd9e7635Sedp {
335fd9e7635Sedp 	brand_t *bp = p->p_zone->zone_brand;
336e9f7cbf0SVamsi Nagineni 	klwp_t *lwp = NULL;
337fd9e7635Sedp 	ASSERT(bp != NULL);
338e9f7cbf0SVamsi Nagineni 	ASSERT(!no_lwps || (p->p_tlist == NULL));
340fd9e7635Sedp 	/*
341e9f7cbf0SVamsi Nagineni 	 * If called from exec_common() or proc_exit(),
342e9f7cbf0SVamsi Nagineni 	 * we know the process is single-threaded.
343e9f7cbf0SVamsi Nagineni 	 * If called from fork_fail, p_tlist is NULL.
344fd9e7635Sedp 	 */
345e9f7cbf0SVamsi Nagineni 	if (!no_lwps) {
346e9f7cbf0SVamsi Nagineni 		ASSERT(p->p_tlist == p->p_tlist->t_forw);
347e9f7cbf0SVamsi Nagineni 		lwp = p->p_tlist->t_lwp;
348e9f7cbf0SVamsi Nagineni 	}
350fd9e7635Sedp 	ASSERT(PROC_IS_BRANDED(p));
351e9f7cbf0SVamsi Nagineni 	BROP(p)->b_proc_exit(p, lwp);
352fd9e7635Sedp 	p->p_brand = &native_brand;
3539acbbeafSnn }
35559f2ff5cSedp #if defined(__sparcv9)
35659f2ff5cSedp /*
357725deb8fSedp  * Currently, only sparc has system level brand syscall interposition.
35859f2ff5cSedp  * On x86 we're able to enable syscall interposition on a per-cpu basis
35959f2ff5cSedp  * when a branded thread is scheduled to run on a cpu.
36059f2ff5cSedp  */
36259f2ff5cSedp /* Local variables needed for dynamic syscall interposition support */
36359f2ff5cSedp static uint32_t	syscall_trap_patch_instr_orig;
36459f2ff5cSedp static uint32_t	syscall_trap32_patch_instr_orig;
36659f2ff5cSedp /* Trap Table syscall entry hot patch points */
36759f2ff5cSedp extern void	syscall_trap_patch_point(void);
36859f2ff5cSedp extern void	syscall_trap32_patch_point(void);
37059f2ff5cSedp /* Alternate syscall entry handlers used when branded zones are running */
37159f2ff5cSedp extern void	syscall_wrapper(void);
37259f2ff5cSedp extern void	syscall_wrapper32(void);
37459f2ff5cSedp /* Macros used to facilitate sparcv9 instruction generation */
37559f2ff5cSedp #define	BA_A_INSTR	0x30800000	/* ba,a addr */
37659f2ff5cSedp #define	DISP22(from, to) \
37759f2ff5cSedp 	((((uintptr_t)(to) - (uintptr_t)(from)) >> 2) & 0x3fffff)
37959f2ff5cSedp /*ARGSUSED*/
380725deb8fSedp static void
brand_plat_interposition_enable(void)381725deb8fSedp brand_plat_interposition_enable(void)
38259f2ff5cSedp {
383725deb8fSedp 	ASSERT(MUTEX_HELD(&brand_list_lock));
38559f2ff5cSedp 	/*
38659f2ff5cSedp 	 * Before we hot patch the kernel save the current instructions
387725deb8fSedp 	 * so that we can restore them later.
38859f2ff5cSedp 	 */
38959f2ff5cSedp 	syscall_trap_patch_instr_orig =
39059f2ff5cSedp 	    *(uint32_t *)syscall_trap_patch_point;
39159f2ff5cSedp 	syscall_trap32_patch_instr_orig =
39259f2ff5cSedp 	    *(uint32_t *)syscall_trap32_patch_point;
39459f2ff5cSedp 	/*
39559f2ff5cSedp 	 * Modify the trap table at the patch points.
39659f2ff5cSedp 	 *
39759f2ff5cSedp 	 * We basically replace the first instruction at the patch
39859f2ff5cSedp 	 * point with a ba,a instruction that will transfer control
39959f2ff5cSedp 	 * to syscall_wrapper or syscall_wrapper32 for 64-bit and
40059f2ff5cSedp 	 * 32-bit syscalls respectively.  It's important to note that
40159f2ff5cSedp 	 * the annul bit is set in the branch so we don't execute
40259f2ff5cSedp 	 * the instruction directly following the one we're patching
40359f2ff5cSedp 	 * during the branch's delay slot.
40459f2ff5cSedp 	 *
40559f2ff5cSedp 	 * It also doesn't matter that we're not atomically updating both
40659f2ff5cSedp 	 * the 64 and 32 bit syscall paths at the same time since there's
40759f2ff5cSedp 	 * no actual branded processes running on the system yet.
40859f2ff5cSedp 	 */
40959f2ff5cSedp 	hot_patch_kernel_text((caddr_t)syscall_trap_patch_point,
41059f2ff5cSedp 	    BA_A_INSTR | DISP22(syscall_trap_patch_point, syscall_wrapper),
41159f2ff5cSedp 	    4);
41259f2ff5cSedp 	hot_patch_kernel_text((caddr_t)syscall_trap32_patch_point,
41359f2ff5cSedp 	    BA_A_INSTR | DISP22(syscall_trap32_patch_point, syscall_wrapper32),
41459f2ff5cSedp 	    4);
41559f2ff5cSedp }
41759f2ff5cSedp /*ARGSUSED*/
418725deb8fSedp static void
brand_plat_interposition_disable(void)419725deb8fSedp brand_plat_interposition_disable(void)
42059f2ff5cSedp {
421725deb8fSedp 	ASSERT(MUTEX_HELD(&brand_list_lock));
42359f2ff5cSedp 	/*
42459f2ff5cSedp 	 * Restore the original instructions at the trap table syscall
42559f2ff5cSedp 	 * patch points to disable the brand syscall interposition
42659f2ff5cSedp 	 * mechanism.
42759f2ff5cSedp 	 */
42859f2ff5cSedp 	hot_patch_kernel_text((caddr_t)syscall_trap_patch_point,
42959f2ff5cSedp 	    syscall_trap_patch_instr_orig, 4);
43059f2ff5cSedp 	hot_patch_kernel_text((caddr_t)syscall_trap32_patch_point,
43159f2ff5cSedp 	    syscall_trap32_patch_instr_orig, 4);
43259f2ff5cSedp }
43359f2ff5cSedp #endif /* __sparcv9 */
43580e2ca85S /*
43680e2ca85S  * The following functions can be shared among kernel brand modules which
43780e2ca85S  * implement Solaris-derived brands, all of which need to do similar tasks
43880e2ca85S  * to manage the brand.
43980e2ca85S  */
44180e2ca85S #if defined(_LP64)
44280e2ca85S static void
Ehdr32to64(Elf32_Ehdr * src,Ehdr * dst)44380e2ca85S Ehdr32to64(Elf32_Ehdr *src, Ehdr *dst)
44480e2ca85S {
44580e2ca85S 	bcopy(src->e_ident, dst->e_ident, sizeof (src->e_ident));
44680e2ca85S 	dst->e_type =		src->e_type;
44780e2ca85S 	dst->e_machine =	src->e_machine;
44880e2ca85S 	dst->e_version =	src->e_version;
44980e2ca85S 	dst->e_entry =		src->e_entry;
45080e2ca85S 	dst->e_phoff =		src->e_phoff;
45180e2ca85S 	dst->e_shoff =		src->e_shoff;
45280e2ca85S 	dst->e_flags =		src->e_flags;
45380e2ca85S 	dst->e_ehsize =		src->e_ehsize;
45480e2ca85S 	dst->e_phentsize =	src->e_phentsize;
45580e2ca85S 	dst->e_phnum =		src->e_phnum;
45680e2ca85S 	dst->e_shentsize =	src->e_shentsize;
45780e2ca85S 	dst->e_shnum =		src->e_shnum;
45880e2ca85S 	dst->e_shstrndx =	src->e_shstrndx;
45980e2ca85S }
46080e2ca85S #endif /* _LP64 */
46280e2ca85S /*
46380e2ca85S  * Return -1 if the cmd was not handled by this function.
46480e2ca85S  */
46580e2ca85S /*ARGSUSED*/
46680e2ca85S int
brand_solaris_cmd(int cmd,uintptr_t arg1,uintptr_t arg2,uintptr_t arg3,struct brand * pbrand,int brandvers)46780e2ca85S brand_solaris_cmd(int cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
46880e2ca85S     struct brand *pbrand, int brandvers)
46980e2ca85S {
47080e2ca85S 	brand_proc_data_t	*spd;
47180e2ca85S 	brand_proc_reg_t	reg;