]>
Commit | Line | Data |
---|---|---|
5ba3f43e A |
1 | /* |
2 | * Copyright (c) 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 | #include <debug.h> | |
29 | ||
30 | #include <types.h> | |
31 | ||
32 | #include <mach/mach_types.h> | |
33 | #include <mach/thread_status.h> | |
34 | #include <mach/vm_types.h> | |
35 | ||
36 | #include <kern/kern_types.h> | |
37 | #include <kern/task.h> | |
38 | #include <kern/thread.h> | |
39 | #include <kern/misc_protos.h> | |
40 | #include <kern/mach_param.h> | |
41 | #include <kern/spl.h> | |
42 | #include <kern/machine.h> | |
43 | #include <kern/kalloc.h> | |
44 | #include <kern/kpc.h> | |
45 | ||
46 | #include <arm/proc_reg.h> | |
47 | #include <arm/cpu_data_internal.h> | |
48 | #include <arm/misc_protos.h> | |
49 | #include <arm/cpuid.h> | |
50 | ||
51 | #include <vm/vm_map.h> | |
52 | #include <vm/vm_protos.h> | |
53 | ||
54 | #include <sys/kdebug.h> | |
55 | ||
56 | extern int debug_task; | |
57 | ||
58 | zone_t ads_zone; /* zone for debug_state area */ | |
59 | ||
60 | /* | |
61 | * Routine: consider_machine_collect | |
62 | * | |
63 | */ | |
64 | void | |
65 | consider_machine_collect(void) | |
66 | { | |
67 | pmap_gc(); | |
68 | } | |
69 | ||
70 | /* | |
71 | * Routine: consider_machine_adjust | |
72 | * | |
73 | */ | |
74 | void | |
75 | consider_machine_adjust(void) | |
76 | { | |
77 | } | |
78 | ||
79 | /* | |
80 | * Routine: machine_switch_context | |
81 | * | |
82 | */ | |
83 | thread_t | |
84 | machine_switch_context( | |
85 | thread_t old, | |
86 | thread_continue_t continuation, | |
87 | thread_t new) | |
88 | { | |
89 | thread_t retval; | |
90 | cpu_data_t *cpu_data_ptr; | |
91 | ||
92 | #define machine_switch_context_kprintf(x...) /* kprintf("machine_switch_con | |
93 | * text: " x) */ | |
94 | ||
95 | cpu_data_ptr = getCpuDatap(); | |
96 | if (old == new) | |
97 | panic("machine_switch_context"); | |
98 | ||
99 | kpc_off_cpu(old); | |
100 | ||
101 | pmap_set_pmap(new->map->pmap, new); | |
102 | ||
103 | new->machine.CpuDatap = cpu_data_ptr; | |
104 | ||
105 | machine_switch_context_kprintf("old= %x contination = %x new = %x\n", old, continuation, new); | |
106 | retval = Switch_context(old, continuation, new); | |
107 | assert(retval != NULL); | |
108 | ||
109 | return retval; | |
110 | } | |
111 | ||
112 | /* | |
113 | * Routine: machine_thread_create | |
114 | * | |
115 | */ | |
116 | kern_return_t | |
117 | machine_thread_create( | |
118 | thread_t thread, | |
119 | #if !__ARM_USER_PROTECT__ | |
120 | __unused | |
121 | #endif | |
122 | task_t task) | |
123 | { | |
124 | ||
125 | #define machine_thread_create_kprintf(x...) /* kprintf("machine_thread_create: " x) */ | |
126 | ||
127 | machine_thread_create_kprintf("thread = %x\n", thread); | |
128 | ||
129 | if (current_thread() != thread) { | |
130 | thread->machine.CpuDatap = (cpu_data_t *)0; | |
131 | } | |
132 | thread->machine.preemption_count = 0; | |
133 | thread->machine.cthread_self = 0; | |
134 | thread->machine.cthread_data = 0; | |
135 | #if __ARM_USER_PROTECT__ | |
136 | { | |
137 | struct pmap *new_pmap = vm_map_pmap(task->map); | |
138 | ||
139 | thread->machine.kptw_ttb = ((unsigned int) kernel_pmap->ttep) | TTBR_SETUP; | |
140 | thread->machine.asid = new_pmap->asid; | |
141 | if (new_pmap->tte_index_max == NTTES) { | |
142 | thread->machine.uptw_ttc = 2; | |
143 | thread->machine.uptw_ttb = ((unsigned int) new_pmap->ttep) | TTBR_SETUP; | |
144 | } else { | |
145 | thread->machine.uptw_ttc = 1; | |
146 | thread->machine.uptw_ttb = ((unsigned int) new_pmap->ttep ) | TTBR_SETUP; | |
147 | } | |
148 | } | |
149 | #endif | |
150 | machine_thread_state_initialize(thread); | |
151 | ||
152 | return (KERN_SUCCESS); | |
153 | } | |
154 | ||
155 | /* | |
156 | * Routine: machine_thread_destroy | |
157 | * | |
158 | */ | |
159 | void | |
160 | machine_thread_destroy( | |
161 | thread_t thread) | |
162 | { | |
163 | ||
164 | if (thread->machine.DebugData != NULL) { | |
165 | if (thread->machine.DebugData == getCpuDatap()->cpu_user_debug) | |
166 | arm_debug_set(NULL); | |
167 | zfree(ads_zone, thread->machine.DebugData); | |
168 | } | |
169 | } | |
170 | ||
171 | ||
172 | /* | |
173 | * Routine: machine_thread_init | |
174 | * | |
175 | */ | |
176 | void | |
177 | machine_thread_init(void) | |
178 | { | |
179 | ads_zone = zinit(sizeof(arm_debug_state_t), | |
180 | THREAD_CHUNK * (sizeof(arm_debug_state_t)), | |
181 | THREAD_CHUNK * (sizeof(arm_debug_state_t)), | |
182 | "arm debug state"); | |
183 | } | |
184 | ||
185 | ||
186 | /* | |
187 | * Routine: get_useraddr | |
188 | * | |
189 | */ | |
190 | user_addr_t | |
191 | get_useraddr() | |
192 | { | |
193 | return (current_thread()->machine.PcbData.pc); | |
194 | } | |
195 | ||
196 | /* | |
197 | * Routine: machine_stack_detach | |
198 | * | |
199 | */ | |
200 | vm_offset_t | |
201 | machine_stack_detach( | |
202 | thread_t thread) | |
203 | { | |
204 | vm_offset_t stack; | |
205 | ||
206 | KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_SCHED, MACH_STACK_DETACH), | |
207 | (uintptr_t)thread_tid(thread), thread->priority, thread->sched_pri, 0, 0); | |
208 | ||
209 | stack = thread->kernel_stack; | |
210 | thread->kernel_stack = 0; | |
211 | thread->machine.kstackptr = 0; | |
212 | ||
213 | return (stack); | |
214 | } | |
215 | ||
216 | ||
217 | /* | |
218 | * Routine: machine_stack_attach | |
219 | * | |
220 | */ | |
221 | void | |
222 | machine_stack_attach( | |
223 | thread_t thread, | |
224 | vm_offset_t stack) | |
225 | { | |
226 | struct arm_saved_state *savestate; | |
227 | ||
228 | #define machine_stack_attach_kprintf(x...) /* kprintf("machine_stack_attach: " x) */ | |
229 | ||
230 | KERNEL_DEBUG(MACHDBG_CODE(DBG_MACH_SCHED, MACH_STACK_ATTACH), | |
231 | (uintptr_t)thread_tid(thread), thread->priority, thread->sched_pri, 0, 0); | |
232 | ||
233 | thread->kernel_stack = stack; | |
234 | thread->machine.kstackptr = stack + kernel_stack_size - sizeof(struct thread_kernel_state); | |
235 | thread_initialize_kernel_state(thread); | |
236 | savestate = (struct arm_saved_state *) thread->machine.kstackptr; | |
237 | ||
238 | savestate->lr = (uint32_t) thread_continue; | |
239 | savestate->sp = thread->machine.kstackptr; | |
240 | savestate->r[7] = 0x0UL; | |
241 | savestate->r[9] = (uint32_t) NULL; | |
242 | savestate->cpsr = PSR_SVC_MODE | PSR_INTMASK; | |
243 | machine_stack_attach_kprintf("thread = %x pc = %x, sp = %x\n", thread, savestate->lr, savestate->sp); | |
244 | } | |
245 | ||
246 | ||
247 | /* | |
248 | * Routine: machine_stack_handoff | |
249 | * | |
250 | */ | |
251 | void | |
252 | machine_stack_handoff( | |
253 | thread_t old, | |
254 | thread_t new) | |
255 | { | |
256 | vm_offset_t stack; | |
257 | cpu_data_t *cpu_data_ptr; | |
258 | ||
259 | kpc_off_cpu(old); | |
260 | ||
261 | stack = machine_stack_detach(old); | |
262 | cpu_data_ptr = getCpuDatap(); | |
263 | new->kernel_stack = stack; | |
264 | new->machine.kstackptr = stack + kernel_stack_size - sizeof(struct thread_kernel_state); | |
265 | if (stack == old->reserved_stack) { | |
266 | assert(new->reserved_stack); | |
267 | old->reserved_stack = new->reserved_stack; | |
268 | new->reserved_stack = stack; | |
269 | } | |
270 | ||
271 | pmap_set_pmap(new->map->pmap, new); | |
272 | new->machine.CpuDatap = cpu_data_ptr; | |
273 | machine_set_current_thread(new); | |
274 | thread_initialize_kernel_state(new); | |
275 | ||
276 | return; | |
277 | } | |
278 | ||
279 | ||
280 | /* | |
281 | * Routine: call_continuation | |
282 | * | |
283 | */ | |
284 | void | |
285 | call_continuation( | |
286 | thread_continue_t continuation, | |
287 | void *parameter, | |
d9a64523 A |
288 | wait_result_t wresult, |
289 | boolean_t enable_interrupts) | |
5ba3f43e A |
290 | { |
291 | #define call_continuation_kprintf(x...) /* kprintf("call_continuation_kprintf: | |
292 | * " x) */ | |
293 | ||
294 | call_continuation_kprintf("thread = %x continuation = %x, stack = %x\n", current_thread(), continuation, current_thread()->machine.kstackptr); | |
d9a64523 | 295 | Call_continuation(continuation, parameter, wresult, enable_interrupts); |
5ba3f43e A |
296 | } |
297 | ||
298 | void arm_debug_set(arm_debug_state_t *debug_state) | |
299 | { | |
300 | /* If this CPU supports the memory-mapped debug interface, use it, otherwise | |
301 | * attempt the Extended CP14 interface. The two routines need to be kept in sync, | |
302 | * functionality-wise. | |
303 | */ | |
304 | struct cpu_data *cpu_data_ptr; | |
305 | arm_debug_info_t *debug_info = arm_debug_info(); | |
306 | boolean_t intr; | |
307 | ||
308 | intr = ml_set_interrupts_enabled(FALSE); | |
309 | cpu_data_ptr = getCpuDatap(); | |
310 | ||
311 | // Set current user debug | |
312 | cpu_data_ptr->cpu_user_debug = debug_state; | |
313 | ||
314 | if (debug_info->memory_mapped_core_debug) { | |
315 | int i; | |
316 | uintptr_t debug_map = cpu_data_ptr->cpu_debug_interface_map; | |
317 | ||
318 | // unlock debug registers | |
319 | *(volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGLAR) = ARM_DBG_LOCK_ACCESS_KEY; | |
320 | ||
321 | // read DBGPRSR to clear the sticky power-down bit (necessary to access debug registers) | |
322 | *(volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGPRSR); | |
323 | ||
324 | // enable monitor mode (needed to set and use debug registers) | |
325 | *(volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGDSCR) |= ARM_DBGDSCR_MDBGEN; | |
326 | ||
327 | // first turn off all breakpoints/watchpoints | |
328 | for (i = 0; i < 16; i++) { | |
329 | ((volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGBCR))[i] = 0; | |
330 | ((volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGWCR))[i] = 0; | |
331 | } | |
332 | ||
333 | // if (debug_state == NULL) disable monitor mode | |
334 | if (debug_state == NULL) { | |
335 | *(volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGDSCR) &= ~ARM_DBGDSCR_MDBGEN; | |
336 | } else { | |
337 | for (i = 0; i < 16; i++) { | |
338 | ((volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGBVR))[i] = debug_state->bvr[i]; | |
339 | ((volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGBCR))[i] = debug_state->bcr[i]; | |
340 | ((volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGWVR))[i] = debug_state->wvr[i]; | |
341 | ((volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGWCR))[i] = debug_state->wcr[i]; | |
342 | } | |
343 | } | |
344 | ||
345 | // lock debug registers | |
346 | *(volatile uint32_t *)(debug_map + ARM_DEBUG_OFFSET_DBGLAR) = 0; | |
347 | ||
348 | } else if (debug_info->coprocessor_core_debug) { | |
349 | arm_debug_set_cp14(debug_state); | |
350 | } | |
351 | ||
352 | (void) ml_set_interrupts_enabled(intr); | |
353 | ||
354 | return; | |
355 | } | |
356 | ||
357 | /* | |
358 | * Duplicate one arm_debug_state_t to another. "all" parameter | |
359 | * is ignored in the case of ARM -- Is this the right assumption? | |
360 | */ | |
361 | void | |
362 | copy_debug_state( | |
363 | arm_debug_state_t *src, | |
364 | arm_debug_state_t *target, | |
365 | __unused boolean_t all) | |
366 | { | |
367 | bcopy(src, target, sizeof(arm_debug_state_t)); | |
368 | } | |
369 | ||
370 | kern_return_t | |
371 | machine_thread_set_tsd_base( | |
372 | thread_t thread, | |
373 | mach_vm_offset_t tsd_base) | |
374 | { | |
375 | ||
376 | if (thread->task == kernel_task) { | |
377 | return KERN_INVALID_ARGUMENT; | |
378 | } | |
379 | ||
380 | if (tsd_base & 0x3) { | |
381 | return KERN_INVALID_ARGUMENT; | |
382 | } | |
383 | ||
384 | if (tsd_base > UINT32_MAX) | |
385 | tsd_base = 0ULL; | |
386 | ||
387 | thread->machine.cthread_self = tsd_base; | |
388 | ||
389 | /* For current thread, make the TSD base active immediately */ | |
390 | if (thread == current_thread()) { | |
391 | ||
392 | mp_disable_preemption(); | |
393 | __asm__ volatile( | |
394 | "mrc p15, 0, r6, c13, c0, 3\n" | |
395 | "and r6, r6, #3\n" | |
396 | "orr r6, r6, %0\n" | |
397 | "mcr p15, 0, r6, c13, c0, 3\n" | |
398 | : /* output */ | |
399 | : "r"((uint32_t)tsd_base) /* input */ | |
400 | : "r6" /* clobbered register */ | |
401 | ); | |
402 | mp_enable_preemption(); | |
403 | ||
404 | } | |
405 | ||
406 | return KERN_SUCCESS; | |
407 | } |