1/*
2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
49 *  School of Computer Science
50 *  Carnegie Mellon University
51 *  Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56/*
57 */
58
59/*
60 *	kern/ipc_host.c
61 *
62 *	Routines to implement host ports.
63 */
64#include <mach/message.h>
65#include <mach/mach_traps.h>
66#include <mach/mach_host_server.h>
67#include <mach/host_priv_server.h>
68#include <kern/host.h>
69#include <kern/processor.h>
70#include <kern/task.h>
71#include <kern/thread.h>
72#include <kern/ipc_host.h>
73#include <kern/ipc_kobject.h>
74#include <kern/misc_protos.h>
75#include <kern/spl.h>
76#include <ipc/ipc_port.h>
77#include <ipc/ipc_space.h>
78
79/*
80 * Forward declarations
81 */
82
83boolean_t
84ref_pset_port_locked(
85	ipc_port_t port, boolean_t matchn, processor_set_t *ppset);
86
87/*
88 *	ipc_host_init: set up various things.
89 */
90
91extern lck_grp_t		host_notify_lock_grp;
92extern lck_attr_t		host_notify_lock_attr;
93
94void ipc_host_init(void)
95{
96	ipc_port_t	port;
97	int i;
98
99	lck_mtx_init(&realhost.lock, &host_notify_lock_grp, &host_notify_lock_attr);
100
101	/*
102	 *	Allocate and set up the two host ports.
103	 */
104	port = ipc_port_alloc_kernel();
105	if (port == IP_NULL)
106		panic("ipc_host_init");
107
108	ipc_kobject_set(port, (ipc_kobject_t) &realhost, IKOT_HOST_SECURITY);
109	kernel_set_special_port(&realhost, HOST_SECURITY_PORT,
110				ipc_port_make_send(port));
111
112	port = ipc_port_alloc_kernel();
113	if (port == IP_NULL)
114		panic("ipc_host_init");
115
116	ipc_kobject_set(port, (ipc_kobject_t) &realhost, IKOT_HOST);
117	kernel_set_special_port(&realhost, HOST_PORT,
118				ipc_port_make_send(port));
119
120	port = ipc_port_alloc_kernel();
121	if (port == IP_NULL)
122		panic("ipc_host_init");
123
124	ipc_kobject_set(port, (ipc_kobject_t) &realhost, IKOT_HOST_PRIV);
125	kernel_set_special_port(&realhost, HOST_PRIV_PORT,
126				ipc_port_make_send(port));
127
128	/* the rest of the special ports will be set up later */
129
130	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
131			realhost.exc_actions[i].port = IP_NULL;
132		}/* for */
133
134	/*
135	 *	Set up ipc for default processor set.
136	 */
137	ipc_pset_init(&pset0);
138	ipc_pset_enable(&pset0);
139
140	/*
141	 *	And for master processor
142	 */
143	ipc_processor_init(master_processor);
144	ipc_processor_enable(master_processor);
145}
146
147/*
148 *	Routine:	host_self_trap [mach trap]
149 *	Purpose:
150 *		Give the caller send rights for his own host port.
151 *	Conditions:
152 *		Nothing locked.
153 *	Returns:
154 *		MACH_PORT_NULL if there are any resource failures
155 *		or other errors.
156 */
157
158mach_port_name_t
159host_self_trap(
160	__unused struct host_self_trap_args *args)
161{
162	ipc_port_t sright;
163	mach_port_name_t name;
164
165	sright = ipc_port_copy_send(current_task()->itk_host);
166	name = ipc_port_copyout_send(sright, current_space());
167	return name;
168}
169
170/*
171 *	ipc_processor_init:
172 *
173 *	Initialize ipc access to processor by allocating port.
174 */
175
176void
177ipc_processor_init(
178	processor_t	processor)
179{
180	ipc_port_t	port;
181
182	port = ipc_port_alloc_kernel();
183	if (port == IP_NULL)
184		panic("ipc_processor_init");
185	processor->processor_self = port;
186}
187
188/*
189 *	ipc_processor_enable:
190 *
191 *	Enable ipc control of processor by setting port object.
192 */
193void
194ipc_processor_enable(
195	processor_t	processor)
196{
197	ipc_port_t	myport;
198
199	myport = processor->processor_self;
200	ipc_kobject_set(myport, (ipc_kobject_t) processor, IKOT_PROCESSOR);
201}
202
203/*
204 *	ipc_pset_init:
205 *
206 *	Initialize ipc control of a processor set by allocating its ports.
207 */
208
209void
210ipc_pset_init(
211	processor_set_t		pset)
212{
213	ipc_port_t	port;
214
215	port = ipc_port_alloc_kernel();
216	if (port == IP_NULL)
217		panic("ipc_pset_init");
218	pset->pset_self = port;
219
220	port = ipc_port_alloc_kernel();
221	if (port == IP_NULL)
222		panic("ipc_pset_init");
223	pset->pset_name_self = port;
224}
225
226/*
227 *	ipc_pset_enable:
228 *
229 *	Enable ipc access to a processor set.
230 */
231void
232ipc_pset_enable(
233	processor_set_t		pset)
234{
235	ipc_kobject_set(pset->pset_self, (ipc_kobject_t) pset, IKOT_PSET);
236	ipc_kobject_set(pset->pset_name_self, (ipc_kobject_t) pset, IKOT_PSET_NAME);
237}
238
239/*
240 *	processor_set_default:
241 *
242 *	Return ports for manipulating default_processor set.
243 */
244kern_return_t
245processor_set_default(
246	host_t			host,
247	processor_set_t		*pset)
248{
249	if (host == HOST_NULL)
250		return(KERN_INVALID_ARGUMENT);
251
252	*pset = &pset0;
253
254	return (KERN_SUCCESS);
255}
256
257/*
258 *	Routine:	convert_port_to_host
259 *	Purpose:
260 *		Convert from a port to a host.
261 *		Doesn't consume the port ref; the host produced may be null.
262 *	Conditions:
263 *		Nothing locked.
264 */
265
266host_t
267convert_port_to_host(
268	ipc_port_t	port)
269{
270	host_t host = HOST_NULL;
271
272	if (IP_VALID(port)) {
273		ip_lock(port);
274		if (ip_active(port) &&
275		    ((ip_kotype(port) == IKOT_HOST) ||
276		     (ip_kotype(port) == IKOT_HOST_PRIV)
277		     ))
278			host = (host_t) port->ip_kobject;
279		ip_unlock(port);
280	}
281
282	return host;
283}
284
285/*
286 *	Routine:	convert_port_to_host_priv
287 *	Purpose:
288 *		Convert from a port to a host.
289 *		Doesn't consume the port ref; the host produced may be null.
290 *	Conditions:
291 *		Nothing locked.
292 */
293
294host_t
295convert_port_to_host_priv(
296	ipc_port_t	port)
297{
298	host_t host = HOST_NULL;
299
300	if (IP_VALID(port)) {
301		ip_lock(port);
302		if (ip_active(port) &&
303		    (ip_kotype(port) == IKOT_HOST_PRIV))
304			host = (host_t) port->ip_kobject;
305		ip_unlock(port);
306	}
307
308	return host;
309}
310
311/*
312 *	Routine:	convert_port_to_processor
313 *	Purpose:
314 *		Convert from a port to a processor.
315 *		Doesn't consume the port ref;
316 *		the processor produced may be null.
317 *	Conditions:
318 *		Nothing locked.
319 */
320
321processor_t
322convert_port_to_processor(
323	ipc_port_t	port)
324{
325	processor_t processor = PROCESSOR_NULL;
326
327	if (IP_VALID(port)) {
328		ip_lock(port);
329		if (ip_active(port) &&
330		    (ip_kotype(port) == IKOT_PROCESSOR))
331			processor = (processor_t) port->ip_kobject;
332		ip_unlock(port);
333	}
334
335	return processor;
336}
337
338/*
339 *	Routine:	convert_port_to_pset
340 *	Purpose:
341 *		Convert from a port to a pset.
342 *		Doesn't consume the port ref; produces a pset ref,
343 *		which may be null.
344 *	Conditions:
345 *		Nothing locked.
346 */
347
348processor_set_t
349convert_port_to_pset(
350	ipc_port_t	port)
351{
352	boolean_t r;
353	processor_set_t pset = PROCESSOR_SET_NULL;
354
355	r = FALSE;
356	while (!r && IP_VALID(port)) {
357		ip_lock(port);
358		r = ref_pset_port_locked(port, FALSE, &pset);
359		/* port unlocked */
360	}
361	return pset;
362}
363
364/*
365 *	Routine:	convert_port_to_pset_name
366 *	Purpose:
367 *		Convert from a port to a pset.
368 *		Doesn't consume the port ref; produces a pset ref,
369 *		which may be null.
370 *	Conditions:
371 *		Nothing locked.
372 */
373
374processor_set_name_t
375convert_port_to_pset_name(
376	ipc_port_t	port)
377{
378	boolean_t r;
379	processor_set_t pset = PROCESSOR_SET_NULL;
380
381	r = FALSE;
382	while (!r && IP_VALID(port)) {
383		ip_lock(port);
384		r = ref_pset_port_locked(port, TRUE, &pset);
385		/* port unlocked */
386	}
387	return pset;
388}
389
390boolean_t
391ref_pset_port_locked(ipc_port_t port, boolean_t matchn, processor_set_t *ppset)
392{
393	processor_set_t pset;
394
395	pset = PROCESSOR_SET_NULL;
396	if (ip_active(port) &&
397		((ip_kotype(port) == IKOT_PSET) ||
398			(matchn && (ip_kotype(port) == IKOT_PSET_NAME)))) {
399		pset = (processor_set_t) port->ip_kobject;
400	}
401
402	*ppset = pset;
403	ip_unlock(port);
404
405	return (TRUE);
406}
407
408/*
409 *	Routine:	convert_host_to_port
410 *	Purpose:
411 *		Convert from a host to a port.
412 *		Produces a naked send right which may be invalid.
413 *	Conditions:
414 *		Nothing locked.
415 */
416
417ipc_port_t
418convert_host_to_port(
419	host_t		host)
420{
421	ipc_port_t port;
422
423	host_get_host_port(host, &port);
424	return port;
425}
426
427/*
428 *	Routine:	convert_processor_to_port
429 *	Purpose:
430 *		Convert from a processor to a port.
431 *		Produces a naked send right which may be invalid.
432 *		Processors are not reference counted, so nothing to release.
433 *	Conditions:
434 *		Nothing locked.
435 */
436
437ipc_port_t
438convert_processor_to_port(
439	processor_t		processor)
440{
441	ipc_port_t port = processor->processor_self;
442
443	if (port != IP_NULL)
444		port = ipc_port_make_send(port);
445	return port;
446}
447
448/*
449 *	Routine:	convert_pset_to_port
450 *	Purpose:
451 *		Convert from a pset to a port.
452 *		Produces a naked send right which may be invalid.
453 *		Processor sets are not reference counted, so nothing to release.
454 *	Conditions:
455 *		Nothing locked.
456 */
457
458ipc_port_t
459convert_pset_to_port(
460	processor_set_t		pset)
461{
462	ipc_port_t port = pset->pset_self;
463
464	if (port != IP_NULL)
465		port = ipc_port_make_send(port);
466
467	return port;
468}
469
470/*
471 *	Routine:	convert_pset_name_to_port
472 *	Purpose:
473 *		Convert from a pset to a port.
474 *		Produces a naked send right which may be invalid.
475 *		Processor sets are not reference counted, so nothing to release.
476 *	Conditions:
477 *		Nothing locked.
478 */
479
480ipc_port_t
481convert_pset_name_to_port(
482	processor_set_name_t		pset)
483{
484	ipc_port_t port = pset->pset_name_self;
485
486	if (port != IP_NULL)
487		port = ipc_port_make_send(port);
488
489	return port;
490}
491
492/*
493 *	Routine:	convert_port_to_host_security
494 *	Purpose:
495 *		Convert from a port to a host security.
496 *		Doesn't consume the port ref; the port produced may be null.
497 *	Conditions:
498 *		Nothing locked.
499 */
500
501host_t
502convert_port_to_host_security(
503	ipc_port_t port)
504{
505	host_t host = HOST_NULL;
506
507	if (IP_VALID(port)) {
508		ip_lock(port);
509		if (ip_active(port) &&
510		    (ip_kotype(port) == IKOT_HOST_SECURITY))
511			host = (host_t) port->ip_kobject;
512		ip_unlock(port);
513	}
514
515	return host;
516}
517
518/*
519 *	Routine:	host_set_exception_ports [kernel call]
520 *	Purpose:
521 *			Sets the host exception port, flavor and
522 *			behavior for the exception types specified by the mask.
523 *			There will be one send right per exception per valid
524 *			port.
525 *	Conditions:
526 *		Nothing locked.  If successful, consumes
527 *		the supplied send right.
528 *	Returns:
529 *		KERN_SUCCESS		Changed the special port.
530 *		KERN_INVALID_ARGUMENT	The host_priv is not valid,
531 *					Illegal mask bit set.
532 *					Illegal exception behavior
533 */
534kern_return_t
535host_set_exception_ports(
536	host_priv_t				host_priv,
537	exception_mask_t		exception_mask,
538	ipc_port_t			new_port,
539	exception_behavior_t		new_behavior,
540	thread_state_flavor_t		new_flavor)
541{
542	register int	i;
543	ipc_port_t	old_port[EXC_TYPES_COUNT];
544
545	if (host_priv == HOST_PRIV_NULL) {
546		return KERN_INVALID_ARGUMENT;
547	}
548
549	assert(host_priv == &realhost);
550
551	if (exception_mask & ~EXC_MASK_VALID) {
552		return KERN_INVALID_ARGUMENT;
553	}
554
555	if (IP_VALID(new_port)) {
556		switch (new_behavior & ~MACH_EXCEPTION_CODES) {
557		case EXCEPTION_DEFAULT:
558		case EXCEPTION_STATE:
559		case EXCEPTION_STATE_IDENTITY:
560			break;
561		default:
562			return KERN_INVALID_ARGUMENT;
563		}
564	}
565
566	/*
567	 * Check the validity of the thread_state_flavor by calling the
568	 * VALID_THREAD_STATE_FLAVOR architecture dependent macro defined in
569	 * osfmk/mach/ARCHITECTURE/thread_status.h
570	 */
571	if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor))
572		return (KERN_INVALID_ARGUMENT);
573
574	host_lock(host_priv);
575
576	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
577		if (exception_mask & (1 << i)) {
578			old_port[i] = host_priv->exc_actions[i].port;
579			host_priv->exc_actions[i].port =
580				ipc_port_copy_send(new_port);
581			host_priv->exc_actions[i].behavior = new_behavior;
582			host_priv->exc_actions[i].flavor = new_flavor;
583		} else
584			old_port[i] = IP_NULL;
585	}/* for */
586
587	/*
588	 * Consume send rights without any lock held.
589	 */
590	host_unlock(host_priv);
591	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++)
592		if (IP_VALID(old_port[i]))
593			ipc_port_release_send(old_port[i]);
594	if (IP_VALID(new_port))		 /* consume send right */
595		ipc_port_release_send(new_port);
596
597        return KERN_SUCCESS;
598}
599
600/*
601 *	Routine:	host_get_exception_ports [kernel call]
602 *	Purpose:
603 *		Clones a send right for each of the host's exception
604 *		ports specified in the mask and returns the behaviour
605 *		and flavor of said port.
606 *
607 *		Returns upto [in} CountCnt elements.
608 *
609 *	Conditions:
610 *		Nothing locked.
611 *	Returns:
612 *		KERN_SUCCESS		Extracted a send right.
613 *		KERN_INVALID_ARGUMENT	Invalid host_priv specified,
614 *					Invalid special port,
615 *					Illegal mask bit set.
616 *		KERN_FAILURE		The thread is dead.
617 */
618kern_return_t
619host_get_exception_ports(
620	host_priv_t			host_priv,
621	exception_mask_t                exception_mask,
622	exception_mask_array_t		masks,
623	mach_msg_type_number_t		* CountCnt,
624	exception_port_array_t		ports,
625	exception_behavior_array_t      behaviors,
626	thread_state_flavor_array_t     flavors		)
627{
628	unsigned int	i, j, count;
629
630	if (host_priv == HOST_PRIV_NULL)
631		return KERN_INVALID_ARGUMENT;
632
633	if (exception_mask & ~EXC_MASK_VALID) {
634		return KERN_INVALID_ARGUMENT;
635	}
636
637	assert (host_priv == &realhost);
638
639	host_lock(host_priv);
640
641	count = 0;
642
643	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
644		if (exception_mask & (1 << i)) {
645			for (j = 0; j < count; j++) {
646/*
647 *				search for an identical entry, if found
648 *				set corresponding mask for this exception.
649 */
650				if (host_priv->exc_actions[i].port == ports[j] &&
651					host_priv->exc_actions[i].behavior == behaviors[j]
652				  && host_priv->exc_actions[i].flavor == flavors[j])
653				{
654					masks[j] |= (1 << i);
655					break;
656				}
657			}/* for */
658			if (j == count) {
659				masks[j] = (1 << i);
660				ports[j] =
661				  ipc_port_copy_send(host_priv->exc_actions[i].port);
662				behaviors[j] = host_priv->exc_actions[i].behavior;
663				flavors[j] = host_priv->exc_actions[i].flavor;
664				count++;
665				if (count > *CountCnt) {
666					break;
667				}
668			}
669		}
670	}/* for */
671	host_unlock(host_priv);
672
673	*CountCnt = count;
674	return KERN_SUCCESS;
675}
676
677kern_return_t
678host_swap_exception_ports(
679	host_priv_t				host_priv,
680	exception_mask_t		exception_mask,
681	ipc_port_t			new_port,
682	exception_behavior_t		new_behavior,
683	thread_state_flavor_t		new_flavor,
684	exception_mask_array_t		masks,
685	mach_msg_type_number_t		* CountCnt,
686	exception_port_array_t		ports,
687	exception_behavior_array_t      behaviors,
688	thread_state_flavor_array_t     flavors		)
689{
690	unsigned int	i,
691			j,
692			count;
693	ipc_port_t	old_port[EXC_TYPES_COUNT];
694
695	if (host_priv == HOST_PRIV_NULL)
696		return KERN_INVALID_ARGUMENT;
697
698	if (exception_mask & ~EXC_MASK_VALID) {
699		return KERN_INVALID_ARGUMENT;
700	}
701
702	if (IP_VALID(new_port)) {
703		switch (new_behavior) {
704		case EXCEPTION_DEFAULT:
705		case EXCEPTION_STATE:
706		case EXCEPTION_STATE_IDENTITY:
707			break;
708		default:
709			return KERN_INVALID_ARGUMENT;
710		}
711	}
712
713	if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor))
714		return (KERN_INVALID_ARGUMENT);
715
716	host_lock(host_priv);
717
718	assert(EXC_TYPES_COUNT > FIRST_EXCEPTION);
719	for (count=0, i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT && count < *CountCnt; i++) {
720		if (exception_mask & (1 << i)) {
721			for (j = 0; j < count; j++) {
722/*
723 *				search for an identical entry, if found
724 *				set corresponding mask for this exception.
725 */
726				if (host_priv->exc_actions[i].port == ports[j] &&
727				  host_priv->exc_actions[i].behavior == behaviors[j]
728				  && host_priv->exc_actions[i].flavor == flavors[j])
729				{
730					masks[j] |= (1 << i);
731					break;
732				}
733			}/* for */
734			if (j == count) {
735				masks[j] = (1 << i);
736				ports[j] =
737				ipc_port_copy_send(host_priv->exc_actions[i].port);
738				behaviors[j] = host_priv->exc_actions[i].behavior;
739				flavors[j] = host_priv->exc_actions[i].flavor;
740				count++;
741			}
742			old_port[i] = host_priv->exc_actions[i].port;
743			host_priv->exc_actions[i].port =
744				ipc_port_copy_send(new_port);
745			host_priv->exc_actions[i].behavior = new_behavior;
746			host_priv->exc_actions[i].flavor = new_flavor;
747		} else
748			old_port[i] = IP_NULL;
749	}/* for */
750	host_unlock(host_priv);
751
752	/*
753	 * Consume send rights without any lock held.
754	 */
755	while (--i >= FIRST_EXCEPTION) {
756		if (IP_VALID(old_port[i]))
757			ipc_port_release_send(old_port[i]);
758	}
759
760	if (IP_VALID(new_port))		 /* consume send right */
761		ipc_port_release_send(new_port);
762	*CountCnt = count;
763
764	return KERN_SUCCESS;
765}
766