xref: /illumos-gate/usr/src/uts/common/os/brand.c (revision 60405de4d8688d96dd05157c28db3ade5c9bc234)
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