1/*
2 * Copyright (c) 2000-2007 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/lock.h>
71#include <kern/task.h>
72#include <kern/thread.h>
73#include <kern/ipc_host.h>
74#include <kern/ipc_kobject.h>
75#include <kern/misc_protos.h>
76#include <kern/spl.h>
77#include <ipc/ipc_port.h>
78#include <ipc/ipc_space.h>
79
80/*
81 * Forward declarations
82 */
83
84void
85ipc_processor_terminate(
86	processor_t	processor);
87
88void
89ipc_processor_disable(
90	processor_t	processor);
91
92boolean_t
93ref_pset_port_locked(
94	ipc_port_t port, boolean_t matchn, processor_set_t *ppset);
95
96/*
97 *	ipc_host_init: set up various things.
98 */
99
100void ipc_host_init(void)
101{
102	ipc_port_t	port;
103	int i;
104
105	mutex_init(&realhost.lock, 0);
106
107	/*
108	 *	Allocate and set up the two host ports.
109	 */
110	port = ipc_port_alloc_kernel();
111	if (port == IP_NULL)
112		panic("ipc_host_init");
113
114	ipc_kobject_set(port, (ipc_kobject_t) &realhost, IKOT_HOST_SECURITY);
115	kernel_set_special_port(&realhost, HOST_SECURITY_PORT,
116				ipc_port_make_send(port));
117
118	port = ipc_port_alloc_kernel();
119	if (port == IP_NULL)
120		panic("ipc_host_init");
121
122	ipc_kobject_set(port, (ipc_kobject_t) &realhost, IKOT_HOST);
123	kernel_set_special_port(&realhost, HOST_PORT,
124				ipc_port_make_send(port));
125
126	port = ipc_port_alloc_kernel();
127	if (port == IP_NULL)
128		panic("ipc_host_init");
129
130	ipc_kobject_set(port, (ipc_kobject_t) &realhost, IKOT_HOST_PRIV);
131	kernel_set_special_port(&realhost, HOST_PRIV_PORT,
132				ipc_port_make_send(port));
133
134	/* the rest of the special ports will be set up later */
135
136	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
137			realhost.exc_actions[i].port = IP_NULL;
138		}/* for */
139
140	/*
141	 *	Set up ipc for default processor set.
142	 */
143	ipc_pset_init(&pset0);
144	ipc_pset_enable(&pset0);
145
146	/*
147	 *	And for master processor
148	 */
149	ipc_processor_init(master_processor);
150	ipc_processor_enable(master_processor);
151}
152
153/*
154 *	Routine:	host_self_trap [mach trap]
155 *	Purpose:
156 *		Give the caller send rights for his own host port.
157 *	Conditions:
158 *		Nothing locked.
159 *	Returns:
160 *		MACH_PORT_NULL if there are any resource failures
161 *		or other errors.
162 */
163
164mach_port_name_t
165host_self_trap(
166	__unused struct host_self_trap_args *args)
167{
168	ipc_port_t sright;
169	mach_port_name_t name;
170
171	sright = ipc_port_copy_send(current_task()->itk_host);
172	name = ipc_port_copyout_send(sright, current_space());
173	return name;
174}
175
176/*
177 *	ipc_processor_init:
178 *
179 *	Initialize ipc access to processor by allocating port.
180 */
181
182void
183ipc_processor_init(
184	processor_t	processor)
185{
186	ipc_port_t	port;
187
188	port = ipc_port_alloc_kernel();
189	if (port == IP_NULL)
190		panic("ipc_processor_init");
191	processor->processor_self = port;
192}
193
194/*
195 *	ipc_processor_enable:
196 *
197 *	Enable ipc control of processor by setting port object.
198 */
199void
200ipc_processor_enable(
201	processor_t	processor)
202{
203	ipc_port_t	myport;
204
205	myport = processor->processor_self;
206	ipc_kobject_set(myport, (ipc_kobject_t) processor, IKOT_PROCESSOR);
207}
208
209/*
210 *	ipc_processor_disable:
211 *
212 *	Disable ipc control of processor by clearing port object.
213 */
214void
215ipc_processor_disable(
216	processor_t	processor)
217{
218	ipc_port_t	myport;
219
220	myport = processor->processor_self;
221	if (myport == IP_NULL)
222		return;
223	ipc_kobject_set(myport, IKO_NULL, IKOT_NONE);
224}
225
226/*
227 *	ipc_processor_terminate:
228 *
229 *	Processor is off-line.  Destroy ipc control port.
230 */
231void
232ipc_processor_terminate(
233	processor_t	processor)
234{
235	ipc_port_t	myport;
236	spl_t		s;
237
238	s = splsched();
239	processor_lock(processor);
240	myport = processor->processor_self;
241	if (myport == IP_NULL) {
242		processor_unlock(processor);
243		splx(s);
244		return;
245	}
246
247	processor->processor_self = IP_NULL;
248	processor_unlock(processor);
249	splx(s);
250
251	ipc_port_dealloc_kernel(myport);
252}
253
254/*
255 *	ipc_pset_init:
256 *
257 *	Initialize ipc control of a processor set by allocating its ports.
258 */
259
260void
261ipc_pset_init(
262	processor_set_t		pset)
263{
264	ipc_port_t	port;
265
266	port = ipc_port_alloc_kernel();
267	if (port == IP_NULL)
268		panic("ipc_pset_init");
269	pset->pset_self = port;
270
271	port = ipc_port_alloc_kernel();
272	if (port == IP_NULL)
273		panic("ipc_pset_init");
274	pset->pset_name_self = port;
275}
276
277/*
278 *	ipc_pset_enable:
279 *
280 *	Enable ipc access to a processor set.
281 */
282void
283ipc_pset_enable(
284	processor_set_t		pset)
285{
286	ipc_kobject_set(pset->pset_self, (ipc_kobject_t) pset, IKOT_PSET);
287	ipc_kobject_set(pset->pset_name_self, (ipc_kobject_t) pset, IKOT_PSET_NAME);
288}
289
290/*
291 *	processor_set_default:
292 *
293 *	Return ports for manipulating default_processor set.
294 */
295kern_return_t
296processor_set_default(
297	host_t			host,
298	processor_set_t		*pset)
299{
300	if (host == HOST_NULL)
301		return(KERN_INVALID_ARGUMENT);
302
303	*pset = &pset0;
304
305	return (KERN_SUCCESS);
306}
307
308/*
309 *	Routine:	convert_port_to_host
310 *	Purpose:
311 *		Convert from a port to a host.
312 *		Doesn't consume the port ref; the host produced may be null.
313 *	Conditions:
314 *		Nothing locked.
315 */
316
317host_t
318convert_port_to_host(
319	ipc_port_t	port)
320{
321	host_t host = HOST_NULL;
322
323	if (IP_VALID(port)) {
324		ip_lock(port);
325		if (ip_active(port) &&
326		    ((ip_kotype(port) == IKOT_HOST) ||
327		     (ip_kotype(port) == IKOT_HOST_PRIV)
328		     ))
329			host = (host_t) port->ip_kobject;
330		ip_unlock(port);
331	}
332
333	return host;
334}
335
336/*
337 *	Routine:	convert_port_to_host_priv
338 *	Purpose:
339 *		Convert from a port to a host.
340 *		Doesn't consume the port ref; the host produced may be null.
341 *	Conditions:
342 *		Nothing locked.
343 */
344
345host_t
346convert_port_to_host_priv(
347	ipc_port_t	port)
348{
349	host_t host = HOST_NULL;
350
351	if (IP_VALID(port)) {
352		ip_lock(port);
353		if (ip_active(port) &&
354		    (ip_kotype(port) == IKOT_HOST_PRIV))
355			host = (host_t) port->ip_kobject;
356		ip_unlock(port);
357	}
358
359	return host;
360}
361
362/*
363 *	Routine:	convert_port_to_processor
364 *	Purpose:
365 *		Convert from a port to a processor.
366 *		Doesn't consume the port ref;
367 *		the processor produced may be null.
368 *	Conditions:
369 *		Nothing locked.
370 */
371
372processor_t
373convert_port_to_processor(
374	ipc_port_t	port)
375{
376	processor_t processor = PROCESSOR_NULL;
377
378	if (IP_VALID(port)) {
379		ip_lock(port);
380		if (ip_active(port) &&
381		    (ip_kotype(port) == IKOT_PROCESSOR))
382			processor = (processor_t) port->ip_kobject;
383		ip_unlock(port);
384	}
385
386	return processor;
387}
388
389/*
390 *	Routine:	convert_port_to_pset
391 *	Purpose:
392 *		Convert from a port to a pset.
393 *		Doesn't consume the port ref; produces a pset ref,
394 *		which may be null.
395 *	Conditions:
396 *		Nothing locked.
397 */
398
399processor_set_t
400convert_port_to_pset(
401	ipc_port_t	port)
402{
403	boolean_t r;
404	processor_set_t pset = PROCESSOR_SET_NULL;
405
406	r = FALSE;
407	while (!r && IP_VALID(port)) {
408		ip_lock(port);
409		r = ref_pset_port_locked(port, FALSE, &pset);
410		/* port unlocked */
411	}
412	return pset;
413}
414
415/*
416 *	Routine:	convert_port_to_pset_name
417 *	Purpose:
418 *		Convert from a port to a pset.
419 *		Doesn't consume the port ref; produces a pset ref,
420 *		which may be null.
421 *	Conditions:
422 *		Nothing locked.
423 */
424
425processor_set_name_t
426convert_port_to_pset_name(
427	ipc_port_t	port)
428{
429	boolean_t r;
430	processor_set_t pset = PROCESSOR_SET_NULL;
431
432	r = FALSE;
433	while (!r && IP_VALID(port)) {
434		ip_lock(port);
435		r = ref_pset_port_locked(port, TRUE, &pset);
436		/* port unlocked */
437	}
438	return pset;
439}
440
441boolean_t
442ref_pset_port_locked(ipc_port_t port, boolean_t matchn, processor_set_t *ppset)
443{
444	processor_set_t pset;
445
446	pset = PROCESSOR_SET_NULL;
447	if (ip_active(port) &&
448		((ip_kotype(port) == IKOT_PSET) ||
449			(matchn && (ip_kotype(port) == IKOT_PSET_NAME)))) {
450		pset = (processor_set_t) port->ip_kobject;
451	}
452
453	*ppset = pset;
454	ip_unlock(port);
455
456	return (TRUE);
457}
458
459/*
460 *	Routine:	convert_host_to_port
461 *	Purpose:
462 *		Convert from a host to a port.
463 *		Produces a naked send right which may be invalid.
464 *	Conditions:
465 *		Nothing locked.
466 */
467
468ipc_port_t
469convert_host_to_port(
470	host_t		host)
471{
472	ipc_port_t port;
473
474	host_get_host_port(host, &port);
475	return port;
476}
477
478/*
479 *	Routine:	convert_processor_to_port
480 *	Purpose:
481 *		Convert from a processor to a port.
482 *		Produces a naked send right which may be invalid.
483 *	Conditions:
484 *		Nothing locked.
485 */
486
487ipc_port_t
488convert_processor_to_port(
489	processor_t		processor)
490{
491	ipc_port_t port;
492	spl_t	s;
493
494	s = splsched();
495	processor_lock(processor);
496
497	if (processor->processor_self != IP_NULL)
498		port = ipc_port_make_send(processor->processor_self);
499	else
500		port = IP_NULL;
501
502	processor_unlock(processor);
503	splx(s);
504
505	return port;
506}
507
508/*
509 *	Routine:	convert_pset_to_port
510 *	Purpose:
511 *		Convert from a pset to a port.
512 *		Produces a naked send right
513 *		which may be invalid.
514 *	Conditions:
515 *		Nothing locked.
516 */
517
518ipc_port_t
519convert_pset_to_port(
520	processor_set_t		pset)
521{
522	ipc_port_t port = pset->pset_self;
523
524	if (port != IP_NULL)
525		port = ipc_port_make_send(port);
526
527	return port;
528}
529
530/*
531 *	Routine:	convert_pset_name_to_port
532 *	Purpose:
533 *		Convert from a pset to a port.
534 *		Produces a naked send right
535 *		which may be invalid.
536 *	Conditions:
537 *		Nothing locked.
538 */
539
540ipc_port_t
541convert_pset_name_to_port(
542	processor_set_name_t		pset)
543{
544	ipc_port_t port = pset->pset_name_self;
545
546	if (port != IP_NULL)
547		port = ipc_port_make_send(port);
548
549	return port;
550}
551
552/*
553 *	Routine:	convert_port_to_host_security
554 *	Purpose:
555 *		Convert from a port to a host security.
556 *		Doesn't consume the port ref; the port produced may be null.
557 *	Conditions:
558 *		Nothing locked.
559 */
560
561host_t
562convert_port_to_host_security(
563	ipc_port_t port)
564{
565	host_t host = HOST_NULL;
566
567	if (IP_VALID(port)) {
568		ip_lock(port);
569		if (ip_active(port) &&
570		    (ip_kotype(port) == IKOT_HOST_SECURITY))
571			host = (host_t) port->ip_kobject;
572		ip_unlock(port);
573	}
574
575	return host;
576}
577
578/*
579 *	Routine:	host_set_exception_ports [kernel call]
580 *	Purpose:
581 *			Sets the host exception port, flavor and
582 *			behavior for the exception types specified by the mask.
583 *			There will be one send right per exception per valid
584 *			port.
585 *	Conditions:
586 *		Nothing locked.  If successful, consumes
587 *		the supplied send right.
588 *	Returns:
589 *		KERN_SUCCESS		Changed the special port.
590 *		KERN_INVALID_ARGUMENT	The host_priv is not valid,
591 *					Illegal mask bit set.
592 *					Illegal exception behavior
593 */
594kern_return_t
595host_set_exception_ports(
596	host_priv_t				host_priv,
597	exception_mask_t		exception_mask,
598	ipc_port_t			new_port,
599	exception_behavior_t		new_behavior,
600	thread_state_flavor_t		new_flavor)
601{
602	register int	i;
603	ipc_port_t	old_port[EXC_TYPES_COUNT];
604
605	if (host_priv == HOST_PRIV_NULL) {
606		return KERN_INVALID_ARGUMENT;
607	}
608
609	assert(host_priv == &realhost);
610
611	if (exception_mask & ~EXC_MASK_ALL) {
612		return KERN_INVALID_ARGUMENT;
613	}
614
615	if (IP_VALID(new_port)) {
616		switch (new_behavior & ~MACH_EXCEPTION_CODES) {
617		case EXCEPTION_DEFAULT:
618		case EXCEPTION_STATE:
619		case EXCEPTION_STATE_IDENTITY:
620			break;
621		default:
622			return KERN_INVALID_ARGUMENT;
623		}
624	}
625	/* Cannot easily check "new_flavor", but that just means that
626	 * the flavor in the generated exception message might be garbage:
627	 * GIGO
628	 */
629	host_lock(host_priv);
630
631	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
632		if (exception_mask & (1 << i)) {
633			old_port[i] = host_priv->exc_actions[i].port;
634			host_priv->exc_actions[i].port =
635				ipc_port_copy_send(new_port);
636			host_priv->exc_actions[i].behavior = new_behavior;
637			host_priv->exc_actions[i].flavor = new_flavor;
638		} else
639			old_port[i] = IP_NULL;
640	}/* for */
641
642	/*
643	 * Consume send rights without any lock held.
644	 */
645	host_unlock(host_priv);
646	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++)
647		if (IP_VALID(old_port[i]))
648			ipc_port_release_send(old_port[i]);
649	if (IP_VALID(new_port))		 /* consume send right */
650		ipc_port_release_send(new_port);
651
652        return KERN_SUCCESS;
653}
654
655/*
656 *	Routine:	host_get_exception_ports [kernel call]
657 *	Purpose:
658 *		Clones a send right for each of the host's exception
659 *		ports specified in the mask and returns the behaviour
660 *		and flavor of said port.
661 *
662 *		Returns upto [in} CountCnt elements.
663 *
664 *	Conditions:
665 *		Nothing locked.
666 *	Returns:
667 *		KERN_SUCCESS		Extracted a send right.
668 *		KERN_INVALID_ARGUMENT	Invalid host_priv specified,
669 *					Invalid special port,
670 *					Illegal mask bit set.
671 *		KERN_FAILURE		The thread is dead.
672 */
673kern_return_t
674host_get_exception_ports(
675	host_priv_t			host_priv,
676	exception_mask_t                exception_mask,
677	exception_mask_array_t		masks,
678	mach_msg_type_number_t		* CountCnt,
679	exception_port_array_t		ports,
680	exception_behavior_array_t      behaviors,
681	thread_state_flavor_array_t     flavors		)
682{
683	unsigned int	i, j, count;
684
685	if (host_priv == HOST_PRIV_NULL)
686		return KERN_INVALID_ARGUMENT;
687
688	if (exception_mask & ~EXC_MASK_ALL) {
689		return KERN_INVALID_ARGUMENT;
690	}
691
692	assert (host_priv == &realhost);
693
694	host_lock(host_priv);
695
696	count = 0;
697
698	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
699		if (exception_mask & (1 << i)) {
700			for (j = 0; j < count; j++) {
701/*
702 *				search for an identical entry, if found
703 *				set corresponding mask for this exception.
704 */
705				if (host_priv->exc_actions[i].port == ports[j] &&
706					host_priv->exc_actions[i].behavior == behaviors[j]
707				  && host_priv->exc_actions[i].flavor == flavors[j])
708				{
709					masks[j] |= (1 << i);
710					break;
711				}
712			}/* for */
713			if (j == count) {
714				masks[j] = (1 << i);
715				ports[j] =
716				  ipc_port_copy_send(host_priv->exc_actions[i].port);
717				behaviors[j] = host_priv->exc_actions[i].behavior;
718				flavors[j] = host_priv->exc_actions[i].flavor;
719				count++;
720				if (count > *CountCnt) {
721					break;
722				}
723			}
724		}
725	}/* for */
726	host_unlock(host_priv);
727
728	*CountCnt = count;
729	return KERN_SUCCESS;
730}
731
732kern_return_t
733host_swap_exception_ports(
734	host_priv_t				host_priv,
735	exception_mask_t		exception_mask,
736	ipc_port_t			new_port,
737	exception_behavior_t		new_behavior,
738	thread_state_flavor_t		new_flavor,
739	exception_mask_array_t		masks,
740	mach_msg_type_number_t		* CountCnt,
741	exception_port_array_t		ports,
742	exception_behavior_array_t      behaviors,
743	thread_state_flavor_array_t     flavors		)
744{
745	unsigned int	i,
746			j,
747			count;
748	ipc_port_t	old_port[EXC_TYPES_COUNT];
749
750	if (host_priv == HOST_PRIV_NULL)
751		return KERN_INVALID_ARGUMENT;
752
753	if (exception_mask & ~EXC_MASK_ALL) {
754		return KERN_INVALID_ARGUMENT;
755	}
756
757	if (IP_VALID(new_port)) {
758		switch (new_behavior) {
759		case EXCEPTION_DEFAULT:
760		case EXCEPTION_STATE:
761		case EXCEPTION_STATE_IDENTITY:
762			break;
763		default:
764			return KERN_INVALID_ARGUMENT;
765		}
766	}
767	/* Cannot easily check "new_flavor", but that just means that
768	 * the flavor in the generated exception message might be garbage:
769	 * GIGO */
770
771	host_lock(host_priv);
772
773	count = 0;
774
775	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
776		if (exception_mask & (1 << i)) {
777			for (j = 0; j < count; j++) {
778/*
779 *				search for an identical entry, if found
780 *				set corresponding mask for this exception.
781 */
782				if (host_priv->exc_actions[i].port == ports[j] &&
783				  host_priv->exc_actions[i].behavior == behaviors[j]
784				  && host_priv->exc_actions[i].flavor == flavors[j])
785				{
786					masks[j] |= (1 << i);
787					break;
788				}
789			}/* for */
790			if (j == count) {
791				masks[j] = (1 << i);
792				ports[j] =
793				ipc_port_copy_send(host_priv->exc_actions[i].port);
794				behaviors[j] = host_priv->exc_actions[i].behavior;
795				flavors[j] = host_priv->exc_actions[i].flavor;
796				count++;
797			}
798			old_port[i] = host_priv->exc_actions[i].port;
799			host_priv->exc_actions[i].port =
800				ipc_port_copy_send(new_port);
801			host_priv->exc_actions[i].behavior = new_behavior;
802			host_priv->exc_actions[i].flavor = new_flavor;
803			if (count > *CountCnt) {
804				break;
805			}
806		} else
807			old_port[i] = IP_NULL;
808	}/* for */
809	host_unlock(host_priv);
810
811	/*
812	 * Consume send rights without any lock held.
813	 */
814	for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++)
815		if (IP_VALID(old_port[i]))
816			ipc_port_release_send(old_port[i]);
817	if (IP_VALID(new_port))		 /* consume send right */
818		ipc_port_release_send(new_port);
819	*CountCnt = count;
820
821	return KERN_SUCCESS;
822}
823