brand.c revision 2712:f74a135872bc
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)
41struct brand_mach_ops native_mach_ops  = {
42		NULL, NULL
43};
44#else
45struct brand_mach_ops native_mach_ops  = {
46		NULL, NULL, NULL, NULL, NULL, NULL
47};
48#endif
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 * This lock protects the integrity of the brand list.
71 */
72static kmutex_t brand_list_lock;
73
74void
75brand_init()
76{
77	mutex_init(&brand_list_lock, NULL, MUTEX_DEFAULT, NULL);
78	p0.p_brand = &native_brand;
79}
80
81int
82brand_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 */
149int
150brand_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 */
200brand_t *
201brand_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 */
271int
272brand_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
288void
289brand_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
304void
305brand_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