1/*
2 * Copyright (c) 2003-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#include <mach/mach_types.h>
30#include <mach/task.h>
31#include <mach/thread_act.h>
32
33#include <kern/kern_types.h>
34#include <kern/processor.h>
35#include <kern/thread.h>
36#include <kern/kalloc.h>
37
38#include <chud/chud_xnu.h>
39#include <chud/chud_xnu_private.h>
40#include <chud/chud_thread.h>
41
42#include <machine/machine_routines.h>
43
44#include <libkern/OSAtomic.h>
45
46#if KPC
47#include <kern/kpc.h>
48#endif
49
50#if KPERF
51#include <kperf/kperf.h>
52#endif
53
54// include the correct file to find real_ncpus
55#if defined(__i386__) || defined(__x86_64__)
56#	include <i386/mp.h>
57#else
58// fall back on declaring it extern.  The linker will sort us out.
59extern unsigned int real_ncpus;
60#endif
61
62// Mask for supported options
63#define T_CHUD_BIND_OPT_MASK (-1UL)
64
65#if 0
66#pragma mark **** thread binding ****
67#endif
68
69/*
70 * This method will bind a given thread to the requested CPU starting at the
71 * next time quantum.  If the thread is the current thread, this method will
72 * force a thread_block().  The result is that if you call this method on the
73 * current thread, you will be on the requested CPU when this method returns.
74 */
75__private_extern__ kern_return_t
76chudxnu_bind_thread(thread_t thread, int cpu, __unused int options)
77{
78    processor_t proc = NULL;
79
80	if(cpu < 0 || (unsigned int)cpu >= real_ncpus) // sanity check
81		return KERN_FAILURE;
82
83	// temporary restriction until after phase 2 of the scheduler
84	if(thread != current_thread())
85		return KERN_FAILURE;
86
87	proc = cpu_to_processor(cpu);
88
89	/*
90	 * Potentially racey, but mainly to prevent bind to shutdown
91	 * processor.
92	 */
93	if(proc && !(proc->state == PROCESSOR_OFF_LINE) &&
94			!(proc->state == PROCESSOR_SHUTDOWN)) {
95
96		thread_bind(proc);
97
98		/*
99		 * If we're trying to bind the current thread, and
100		 * we're not on the target cpu, and not at interrupt
101		 * context, block the current thread to force a
102		 * reschedule on the target CPU.
103		 */
104		if(thread == current_thread() &&
105			!ml_at_interrupt_context() && cpu_number() != cpu) {
106			(void)thread_block(THREAD_CONTINUE_NULL);
107		}
108		return KERN_SUCCESS;
109	}
110    return KERN_FAILURE;
111}
112
113__private_extern__ kern_return_t
114chudxnu_unbind_thread(thread_t thread, __unused int options)
115{
116	if(thread == current_thread())
117		thread_bind(PROCESSOR_NULL);
118    return KERN_SUCCESS;
119}
120
121__private_extern__ boolean_t
122chudxnu_thread_get_idle(thread_t thread) {
123	/*
124	 * Instantaneous snapshot of the idle state of
125	 * a given thread.
126	 *
127	 * Should be called only on an interrupted or
128	 * suspended thread to avoid a race.
129	 */
130	return ((thread->state & TH_IDLE) == TH_IDLE);
131}
132
133__private_extern__ int
134chudxnu_thread_get_scheduler_state(thread_t thread) {
135	/*
136	 * Instantaneous snapshot of the scheduler state of
137	 * a given thread.
138	 *
139	 * MUST ONLY be called on an interrupted or
140	 * locked thread, to avoid a race.
141	 */
142
143	int state = 0;
144	int schedulerState = (volatile int)(thread->state);
145	processor_t lastProcessor = (volatile processor_t)(thread->last_processor);
146
147	if ((PROCESSOR_NULL != lastProcessor) && (thread == lastProcessor->active_thread)) {
148		state |= CHUDXNU_TS_RUNNING;
149	}
150
151	if (schedulerState & TH_RUN) {
152		state |= CHUDXNU_TS_RUNNABLE;
153	}
154
155	if (schedulerState & TH_WAIT) {
156		state |= CHUDXNU_TS_WAIT;
157	}
158
159	if (schedulerState & TH_UNINT) {
160		state |= CHUDXNU_TS_UNINT;
161	}
162
163	if (schedulerState & TH_SUSP) {
164		state |= CHUDXNU_TS_SUSP;
165	}
166
167	if (schedulerState & TH_TERMINATE) {
168		state |= CHUDXNU_TS_TERMINATE;
169	}
170
171	if (schedulerState & TH_IDLE) {
172		state |= CHUDXNU_TS_IDLE;
173	}
174
175	return state;
176}
177
178#if 0
179#pragma mark **** task and thread info ****
180#endif
181
182__private_extern__ boolean_t
183chudxnu_is_64bit_task(task_t task)
184{
185	return (task_has_64BitAddr(task));
186}
187
188#define THING_TASK		0
189#define THING_THREAD	1
190
191// an exact copy of processor_set_things() except no mig conversion at the end!
192static kern_return_t
193chudxnu_private_processor_set_things(
194	processor_set_t		pset,
195	mach_port_t		**thing_list,
196	mach_msg_type_number_t	*count,
197	int			type)
198{
199	unsigned int actual;	/* this many things */
200	unsigned int maxthings;
201	unsigned int i;
202
203	vm_size_t size, size_needed;
204	void  *addr;
205
206	if (pset == PROCESSOR_SET_NULL || pset != &pset0)
207		return (KERN_INVALID_ARGUMENT);
208
209	size = 0; addr = NULL;
210
211	for (;;) {
212		lck_mtx_lock(&tasks_threads_lock);
213
214		if (type == THING_TASK)
215			maxthings = tasks_count;
216		else
217			maxthings = threads_count;
218
219		/* do we have the memory we need? */
220
221		size_needed = maxthings * sizeof (mach_port_t);
222		if (size_needed <= size)
223			break;
224
225		lck_mtx_unlock(&tasks_threads_lock);
226
227		if (size != 0)
228			kfree(addr, size);
229
230		assert(size_needed > 0);
231		size = size_needed;
232
233		addr = kalloc(size);
234		if (addr == 0)
235			return (KERN_RESOURCE_SHORTAGE);
236	}
237
238	/* OK, have memory and the processor_set is locked & active */
239
240	actual = 0;
241	switch (type) {
242
243	case THING_TASK:
244	{
245		task_t		task, *task_list = (task_t *)addr;
246
247		for (task = (task_t)queue_first(&tasks);
248				!queue_end(&tasks, (queue_entry_t)task);
249					task = (task_t)queue_next(&task->tasks)) {
250			task_reference_internal(task);
251			task_list[actual++] = task;
252		}
253
254		break;
255	}
256
257	case THING_THREAD:
258	{
259		thread_t	thread, *thread_list = (thread_t *)addr;
260
261		for (i = 0, thread = (thread_t)queue_first(&threads);
262				!queue_end(&threads, (queue_entry_t)thread);
263					thread = (thread_t)queue_next(&thread->threads)) {
264			thread_reference_internal(thread);
265			thread_list[actual++] = thread;
266		}
267
268		break;
269	}
270	}
271
272	lck_mtx_unlock(&tasks_threads_lock);
273
274	if (actual < maxthings)
275		size_needed = actual * sizeof (mach_port_t);
276
277	if (actual == 0) {
278		/* no things, so return null pointer and deallocate memory */
279		*thing_list = NULL;
280		*count = 0;
281
282		if (size != 0)
283			kfree(addr, size);
284	}
285	else {
286		/* if we allocated too much, must copy */
287
288		if (size_needed < size) {
289			void *newaddr;
290
291			newaddr = kalloc(size_needed);
292			if (newaddr == 0) {
293				switch (type) {
294
295				case THING_TASK:
296				{
297					task_t		*task_list = (task_t *)addr;
298
299					for (i = 0; i < actual; i++)
300						task_deallocate(task_list[i]);
301					break;
302				}
303
304				case THING_THREAD:
305				{
306					thread_t	*thread_list = (thread_t *)addr;
307
308					for (i = 0; i < actual; i++)
309						thread_deallocate(thread_list[i]);
310					break;
311				}
312				}
313
314				kfree(addr, size);
315				return (KERN_RESOURCE_SHORTAGE);
316			}
317
318			bcopy((void *) addr, (void *) newaddr, size_needed);
319			kfree(addr, size);
320			addr = newaddr;
321		}
322
323		*thing_list = (mach_port_t *)addr;
324		*count = actual;
325	}
326
327	return (KERN_SUCCESS);
328}
329
330// an exact copy of task_threads() except no mig conversion at the end!
331static kern_return_t
332chudxnu_private_task_threads(
333	task_t			task,
334	thread_act_array_t      *threads_out,
335    	mach_msg_type_number_t  *count)
336{
337	mach_msg_type_number_t	actual;
338	thread_t				*thread_list;
339	thread_t				thread;
340	vm_size_t				size, size_needed;
341	void					*addr;
342	unsigned int			i, j;
343
344	if (task == TASK_NULL)
345		return (KERN_INVALID_ARGUMENT);
346
347	size = 0; addr = NULL;
348
349	for (;;) {
350		task_lock(task);
351		if (!task->active) {
352			task_unlock(task);
353
354			if (size != 0)
355				kfree(addr, size);
356
357			return (KERN_FAILURE);
358		}
359
360		actual = task->thread_count;
361
362		/* do we have the memory we need? */
363		size_needed = actual * sizeof (mach_port_t);
364		if (size_needed <= size)
365			break;
366
367		/* unlock the task and allocate more memory */
368		task_unlock(task);
369
370		if (size != 0)
371			kfree(addr, size);
372
373		assert(size_needed > 0);
374		size = size_needed;
375
376		addr = kalloc(size);
377		if (addr == 0)
378			return (KERN_RESOURCE_SHORTAGE);
379	}
380
381	/* OK, have memory and the task is locked & active */
382	thread_list = (thread_t *)addr;
383
384	i = j = 0;
385
386	for (thread = (thread_t)queue_first(&task->threads); i < actual;
387				++i, thread = (thread_t)queue_next(&thread->task_threads)) {
388		thread_reference_internal(thread);
389		thread_list[j++] = thread;
390	}
391
392	assert(queue_end(&task->threads, (queue_entry_t)thread));
393
394	actual = j;
395	size_needed = actual * sizeof (mach_port_t);
396
397	/* can unlock task now that we've got the thread refs */
398	task_unlock(task);
399
400	if (actual == 0) {
401		/* no threads, so return null pointer and deallocate memory */
402
403		*threads_out = NULL;
404		*count = 0;
405
406		if (size != 0)
407			kfree(addr, size);
408	}
409	else {
410		/* if we allocated too much, must copy */
411
412		if (size_needed < size) {
413			void *newaddr;
414
415			newaddr = kalloc(size_needed);
416			if (newaddr == 0) {
417				for (i = 0; i < actual; ++i)
418					thread_deallocate(thread_list[i]);
419				kfree(addr, size);
420				return (KERN_RESOURCE_SHORTAGE);
421			}
422
423			bcopy(addr, newaddr, size_needed);
424			kfree(addr, size);
425			thread_list = (thread_t *)newaddr;
426		}
427
428		*threads_out = thread_list;
429		*count = actual;
430	}
431
432	return (KERN_SUCCESS);
433}
434
435
436__private_extern__ kern_return_t
437chudxnu_all_tasks(
438	task_array_t		*task_list,
439	mach_msg_type_number_t	*count)
440{
441	return chudxnu_private_processor_set_things(&pset0, (mach_port_t **)task_list, count, THING_TASK);
442}
443
444__private_extern__ kern_return_t
445chudxnu_free_task_list(
446	task_array_t		*task_list,
447	mach_msg_type_number_t	*count)
448{
449	vm_size_t size = (*count)*sizeof(mach_port_t);
450	void *addr = *task_list;
451
452	if(addr) {
453		int i, maxCount = *count;
454		for(i=0; i<maxCount; i++) {
455			task_deallocate((*task_list)[i]);
456		}
457		kfree(addr, size);
458		*task_list = NULL;
459		*count = 0;
460		return KERN_SUCCESS;
461	} else {
462		return KERN_FAILURE;
463	}
464}
465__private_extern__ kern_return_t
466chudxnu_all_threads(
467	thread_array_t		*thread_list,
468	mach_msg_type_number_t	*count)
469{
470	return chudxnu_private_processor_set_things(&pset0, (mach_port_t **)thread_list, count, THING_THREAD);
471}
472
473__private_extern__ kern_return_t
474chudxnu_task_threads(
475	task_t task,
476	thread_array_t *thread_list,
477	mach_msg_type_number_t *count)
478{
479	return chudxnu_private_task_threads(task, thread_list, count);
480}
481
482__private_extern__ kern_return_t
483chudxnu_free_thread_list(
484	thread_array_t	*thread_list,
485	mach_msg_type_number_t	*count)
486{
487	vm_size_t size = (*count)*sizeof(mach_port_t);
488	void *addr = *thread_list;
489
490	if(addr) {
491		int i, maxCount = *count;
492		for(i=0; i<maxCount; i++) {
493			thread_deallocate((*thread_list)[i]);
494		}
495		kfree(addr, size);
496		*thread_list = NULL;
497		*count = 0;
498		return KERN_SUCCESS;
499	} else {
500		return KERN_FAILURE;
501	}
502}
503
504__private_extern__ task_t
505chudxnu_current_task(void)
506{
507	return current_task();
508}
509
510__private_extern__ thread_t
511chudxnu_current_thread(void)
512{
513	return current_thread();
514}
515
516__private_extern__ task_t
517chudxnu_task_for_thread(thread_t thread)
518{
519    return get_threadtask(thread);
520}
521
522__private_extern__ kern_return_t
523chudxnu_thread_info(
524	thread_t thread,
525	thread_flavor_t flavor,
526	thread_info_t thread_info_out,
527	mach_msg_type_number_t *thread_info_count)
528{
529	return thread_info(thread, flavor, thread_info_out, thread_info_count);
530}
531
532
533/* thread marking stuff */
534
535__private_extern__ boolean_t
536chudxnu_thread_get_marked(thread_t thread)
537{
538	if(thread)
539		return ((thread->t_chud & T_CHUD_MARKED) != 0);
540	return FALSE;
541}
542
543__private_extern__ boolean_t
544chudxnu_thread_set_marked(thread_t thread, boolean_t new_value)
545{
546	boolean_t old_val;
547
548	if(thread) {
549		if(new_value) {
550			// set the marked bit
551			old_val = OSBitOrAtomic(T_CHUD_MARKED,  &(thread->t_chud));
552		} else {
553			// clear the marked bit
554			old_val = OSBitAndAtomic(~T_CHUD_MARKED,  &(thread->t_chud));
555		}
556		return (old_val & T_CHUD_MARKED) == T_CHUD_MARKED;
557	}
558	return FALSE;
559}
560
561/* XXX: good thing this code is experimental... */
562
563/* external handler */
564extern void (*chudxnu_thread_ast_handler)(thread_t);
565void (*chudxnu_thread_ast_handler)(thread_t) = NULL;
566
567/* AST callback to dispatch to AppleProfile */
568extern void chudxnu_thread_ast(thread_t);
569void
570chudxnu_thread_ast(thread_t thread)
571{
572#if KPC
573	/* check for PMC work */
574	kpc_thread_ast_handler(thread);
575#endif
576
577#if KPERF
578	/* check for kperf work */
579	kperf_thread_ast_handler(thread);
580#endif
581
582	/* atomicness for kdebug events */
583	void (*handler)(thread_t) = chudxnu_thread_ast_handler;
584	if( handler )
585		handler( thread );
586
587	thread->t_chud = 0;
588}
589
590
591
592/* Get and set bits on the thread and trigger an AST handler */
593void chudxnu_set_thread_ast( thread_t thread );
594void
595chudxnu_set_thread_ast( thread_t thread )
596{
597	/* FIXME: only call this on current thread from an interrupt handler for now... */
598	if( thread != current_thread() )
599		panic( "unsafe AST set" );
600
601	act_set_kperf(thread);
602}
603
604/* get and set the thread bits */
605extern uint32_t chudxnu_get_thread_bits( thread_t thread );
606extern void chudxnu_set_thread_bits( thread_t thread, uint32_t bits );
607
608uint32_t
609chudxnu_get_thread_bits( thread_t thread )
610{
611	return thread->t_chud;
612}
613
614void
615chudxnu_set_thread_bits( thread_t thread, uint32_t bits )
616{
617	thread->t_chud = bits;
618}
619
620/* get and set thread dirty bits. so CHUD can track whether the thread
621 * has been dispatched since it last looked. caller must hold the
622 * thread lock
623 */
624boolean_t
625chudxnu_thread_get_dirty(thread_t thread)
626{
627	if( thread->c_switch != thread->chud_c_switch )
628		return TRUE;
629	else
630		return FALSE;
631}
632
633void
634chudxnu_thread_set_dirty(thread_t thread, boolean_t makedirty)
635{
636	if( makedirty )
637		thread->chud_c_switch = thread->c_switch - 1;
638	else
639		thread->chud_c_switch = thread->c_switch;
640}
641