brand.c revision 4141:ddd21f3d4066
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 2007 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 /* !__sparcv9 */
45struct brand_mach_ops native_mach_ops  = {
46		NULL, NULL, NULL, NULL, NULL, NULL
47};
48#endif /* !__sparcv9 */
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 * Used to enable brand platform specific interposition code
71 */
72#pragma	weak	brand_plat_interposition_init
73extern void brand_plat_interposition_init(void);
74
75/*
76 * This lock protects the integrity of the brand list.
77 */
78static kmutex_t brand_list_lock;
79
80void
81brand_init()
82{
83	brand_plat_interposition_init();
84	mutex_init(&brand_list_lock, NULL, MUTEX_DEFAULT, NULL);
85	p0.p_brand = &native_brand;
86}
87
88int
89brand_register(brand_t *brand)
90{
91	struct brand_list *list, *scan;
92
93	if (brand == NULL)
94		return (EINVAL);
95
96	if (is_system_labeled()) {
97		cmn_err(CE_WARN,
98		    "Branded zones are not allowed on labeled systems.");
99		return (EINVAL);
100	}
101
102	if (brand->b_version != SUPPORTED_BRAND_VERSION) {
103		if (brand->b_version < SUPPORTED_BRAND_VERSION) {
104			cmn_err(CE_WARN,
105			    "brand '%s' was built to run on older versions "
106			    "of Solaris.",
107			    brand->b_name);
108		} else {
109			cmn_err(CE_WARN,
110			    "brand '%s' was built to run on a newer version "
111			    "of Solaris.",
112			    brand->b_name);
113		}
114		return (EINVAL);
115	}
116
117	/* Sanity checks */
118	if (brand->b_name == NULL || brand->b_ops == NULL ||
119	    brand->b_ops->b_brandsys == NULL) {
120		cmn_err(CE_WARN, "Malformed brand");
121		return (EINVAL);
122	}
123
124	list = kmem_alloc(sizeof (struct brand_list), KM_SLEEP);
125
126	/* Add the brand to the list of loaded brands. */
127	mutex_enter(&brand_list_lock);
128
129	/*
130	 * Check to be sure we haven't already registered this brand.
131	 */
132	for (scan = brand_list; scan != NULL; scan = scan->bl_next) {
133		if (strcmp(brand->b_name, scan->bl_brand->b_name) == 0) {
134			cmn_err(CE_WARN,
135			    "Invalid attempt to load a second instance of "
136			    "brand %s", brand->b_name);
137			mutex_exit(&brand_list_lock);
138			kmem_free(list, sizeof (struct brand_list));
139			return (EINVAL);
140		}
141	}
142
143	list->bl_brand = brand;
144	list->bl_refcnt = 0;
145	list->bl_next = brand_list;
146	brand_list = list;
147	mutex_exit(&brand_list_lock);
148
149	return (0);
150}
151
152/*
153 * The kernel module implementing this brand is being unloaded, so remove
154 * it from the list of active brands.
155 */
156int
157brand_unregister(brand_t *brand)
158{
159	struct brand_list *list, *prev;
160
161	/* Sanity checks */
162	if (brand == NULL || brand->b_name == NULL) {
163		cmn_err(CE_WARN, "Malformed brand");
164		return (EINVAL);
165	}
166
167	prev = NULL;
168	mutex_enter(&brand_list_lock);
169
170	for (list = brand_list; list != NULL; list = list->bl_next) {
171		if (list->bl_brand == brand)
172			break;
173		prev = list;
174	}
175
176	if (list == NULL) {
177		cmn_err(CE_WARN, "Brand %s wasn't registered", brand->b_name);
178		mutex_exit(&brand_list_lock);
179		return (EINVAL);
180	}
181
182	if (list->bl_refcnt > 0) {
183		cmn_err(CE_WARN, "Unregistering brand %s which is still in use",
184		    brand->b_name);
185		mutex_exit(&brand_list_lock);
186		return (EBUSY);
187	}
188
189	/* Remove brand from the list */
190	if (prev != NULL)
191		prev->bl_next = list->bl_next;
192	else
193		brand_list = list->bl_next;
194
195	mutex_exit(&brand_list_lock);
196
197	kmem_free(list, sizeof (struct brand_list));
198
199	return (0);
200}
201
202/*
203 * Record that a zone of this brand has been instantiated.  If the kernel
204 * module implementing this brand's functionality is not present, this
205 * routine attempts to load the module as a side effect.
206 */
207brand_t *
208brand_register_zone(struct brand_attr *attr)
209{
210	struct brand_list *l = NULL;
211	ddi_modhandle_t	hdl = NULL;
212	char *modname;
213	int err = 0;
214
215	if (is_system_labeled()) {
216		cmn_err(CE_WARN,
217		    "Branded zones are not allowed on labeled systems.");
218		return (NULL);
219	}
220
221	/*
222	 * We make at most two passes through this loop.  The first time
223	 * through, we're looking to see if this is a new user of an
224	 * already loaded brand.  If the brand hasn't been loaded, we
225	 * call ddi_modopen() to force it to be loaded and then make a
226	 * second pass through the list of brands.  If we don't find the
227	 * brand the second time through it means that the modname
228	 * specified in the brand_attr structure doesn't provide the brand
229	 * specified in the brandname field.  This would suggest a bug in
230	 * the brand's config.xml file.  We close the module and return
231	 * 'NULL' to the caller.
232	 */
233	for (;;) {
234		/*
235		 * Search list of loaded brands
236		 */
237		mutex_enter(&brand_list_lock);
238		for (l = brand_list; l != NULL; l = l->bl_next)
239			if (strcmp(attr->ba_brandname,
240			    l->bl_brand->b_name) == 0)
241				break;
242		if ((l != NULL) || (hdl != NULL))
243			break;
244		mutex_exit(&brand_list_lock);
245
246		/*
247		 * We didn't find that the requested brand has been loaded
248		 * yet, so we trigger the load of the appropriate kernel
249		 * module and search the list again.
250		 */
251		modname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
252		(void) strcpy(modname, "brand/");
253		(void) strcat(modname, attr->ba_modname);
254		hdl = ddi_modopen(modname, KRTLD_MODE_FIRST, &err);
255		kmem_free(modname, MAXPATHLEN);
256
257		if (err != 0)
258			return (NULL);
259	}
260
261	/*
262	 * If we found the matching brand, bump its reference count.
263	 */
264	if (l != NULL)
265		l->bl_refcnt++;
266
267	mutex_exit(&brand_list_lock);
268
269	if (hdl != NULL)
270		(void) ddi_modclose(hdl);
271
272	return ((l != NULL) ? l->bl_brand : NULL);
273}
274
275/*
276 * Return the number of zones currently using this brand.
277 */
278int
279brand_zone_count(struct brand *bp)
280{
281	struct brand_list *l;
282	int cnt = 0;
283
284	mutex_enter(&brand_list_lock);
285	for (l = brand_list; l != NULL; l = l->bl_next)
286		if (l->bl_brand == bp) {
287			cnt = l->bl_refcnt;
288			break;
289		}
290	mutex_exit(&brand_list_lock);
291
292	return (cnt);
293}
294
295void
296brand_unregister_zone(struct brand *bp)
297{
298	struct brand_list *list;
299
300	mutex_enter(&brand_list_lock);
301	for (list = brand_list; list != NULL; list = list->bl_next) {
302		if (list->bl_brand == bp) {
303			ASSERT(list->bl_refcnt > 0);
304			list->bl_refcnt--;
305			break;
306		}
307	}
308	mutex_exit(&brand_list_lock);
309}
310
311void
312brand_setbrand(proc_t *p)
313{
314	brand_t *bp = p->p_zone->zone_brand;
315
316	ASSERT(bp != NULL);
317	ASSERT(p->p_brand == &native_brand);
318
319	/*
320	 * We should only be called from exec(), when we know the process
321	 * is single-threaded.
322	 */
323	ASSERT(p->p_tlist == p->p_tlist->t_forw);
324
325	p->p_brand = bp;
326	if (PROC_IS_BRANDED(p)) {
327		BROP(p)->b_setbrand(p);
328		lwp_attach_brand_hdlrs(p->p_tlist->t_lwp);
329	}
330}
331
332#if defined(__sparcv9)
333/*
334 * Currently, only sparc has platform level brand syscall interposition.
335 * On x86 we're able to enable syscall interposition on a per-cpu basis
336 * when a branded thread is scheduled to run on a cpu.
337 */
338
339/* Local variables needed for dynamic syscall interposition support */
340static kmutex_t	brand_interposition_lock;
341static int	brand_interposition_count;
342static uint32_t	syscall_trap_patch_instr_orig;
343static uint32_t	syscall_trap32_patch_instr_orig;
344
345/* Trap Table syscall entry hot patch points */
346extern void	syscall_trap_patch_point(void);
347extern void	syscall_trap32_patch_point(void);
348
349/* Alternate syscall entry handlers used when branded zones are running */
350extern void	syscall_wrapper(void);
351extern void	syscall_wrapper32(void);
352
353/* Macros used to facilitate sparcv9 instruction generation */
354#define	BA_A_INSTR	0x30800000	/* ba,a addr */
355#define	DISP22(from, to) \
356	((((uintptr_t)(to) - (uintptr_t)(from)) >> 2) & 0x3fffff)
357
358void
359brand_plat_interposition_init(void)
360{
361	mutex_init(&brand_interposition_lock, NULL, MUTEX_DEFAULT, NULL);
362	brand_interposition_count = 0;
363}
364
365/*ARGSUSED*/
366void
367brand_plat_interposition_enable(brand_t *bp)
368{
369	ASSERT((bp != NULL) && (bp != &native_brand));
370
371	mutex_enter(&brand_interposition_lock);
372	ASSERT(brand_interposition_count >= 0);
373
374	if (brand_interposition_count++ > 0) {
375		mutex_exit(&brand_interposition_lock);
376		return;
377	}
378
379	/*
380	 * This is the first branded zone that is being enabled on
381	 * this system.
382	 *
383	 * Before we hot patch the kernel save the current instructions
384	 * so that we can restore them if all branded zones on the
385	 * system are shutdown.
386	 */
387	syscall_trap_patch_instr_orig =
388	    *(uint32_t *)syscall_trap_patch_point;
389	syscall_trap32_patch_instr_orig =
390	    *(uint32_t *)syscall_trap32_patch_point;
391
392	/*
393	 * Modify the trap table at the patch points.
394	 *
395	 * We basically replace the first instruction at the patch
396	 * point with a ba,a instruction that will transfer control
397	 * to syscall_wrapper or syscall_wrapper32 for 64-bit and
398	 * 32-bit syscalls respectively.  It's important to note that
399	 * the annul bit is set in the branch so we don't execute
400	 * the instruction directly following the one we're patching
401	 * during the branch's delay slot.
402	 *
403	 * It also doesn't matter that we're not atomically updating both
404	 * the 64 and 32 bit syscall paths at the same time since there's
405	 * no actual branded processes running on the system yet.
406	 */
407	hot_patch_kernel_text((caddr_t)syscall_trap_patch_point,
408	    BA_A_INSTR | DISP22(syscall_trap_patch_point, syscall_wrapper),
409	    4);
410	hot_patch_kernel_text((caddr_t)syscall_trap32_patch_point,
411	    BA_A_INSTR | DISP22(syscall_trap32_patch_point, syscall_wrapper32),
412	    4);
413
414	mutex_exit(&brand_interposition_lock);
415}
416
417/*ARGSUSED*/
418void
419brand_plat_interposition_disable(brand_t *bp)
420{
421	ASSERT((bp != NULL) && (bp != &native_brand));
422
423	mutex_enter(&brand_interposition_lock);
424	ASSERT(brand_interposition_count > 0);
425
426	if (--brand_interposition_count > 0) {
427		mutex_exit(&brand_interposition_lock);
428		return;
429	}
430
431	/*
432	 * The last branded zone on this system has been shutdown.
433	 *
434	 * Restore the original instructions at the trap table syscall
435	 * patch points to disable the brand syscall interposition
436	 * mechanism.
437	 */
438	hot_patch_kernel_text((caddr_t)syscall_trap_patch_point,
439	    syscall_trap_patch_instr_orig, 4);
440	hot_patch_kernel_text((caddr_t)syscall_trap32_patch_point,
441	    syscall_trap32_patch_instr_orig, 4);
442
443	mutex_exit(&brand_interposition_lock);
444}
445#endif /* __sparcv9 */
446