]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * File: ppc/cpu.c | |
24 | * | |
25 | * cpu specific routines | |
26 | */ | |
27 | ||
28 | #include <kern/machine.h> | |
29 | #include <kern/misc_protos.h> | |
30 | #include <kern/thread.h> | |
31 | #include <kern/processor.h> | |
32 | #include <mach/machine.h> | |
33 | #include <mach/processor_info.h> | |
34 | #include <mach/mach_types.h> | |
35 | #include <ppc/proc_reg.h> | |
36 | #include <ppc/misc_protos.h> | |
37 | #include <ppc/machine_routines.h> | |
38 | #include <ppc/machine_cpu.h> | |
39 | #include <ppc/exception.h> | |
40 | #include <pexpert/pexpert.h> | |
41 | //#include <pexpert/ppc/powermac.h> | |
42 | ||
43 | /* TODO: BOGUS TO BE REMOVED */ | |
44 | int real_ncpus = 1; | |
45 | ||
46 | int wncpu = NCPUS; | |
47 | resethandler_t resethandler_target; | |
48 | ||
49 | #define MMCR0_SUPPORT_MASK 0xf83f1fff | |
50 | #define MMCR1_SUPPORT_MASK 0xffc00000 | |
51 | #define MMCR2_SUPPORT_MASK 0x80000000 | |
52 | ||
53 | extern int debugger_pending[NCPUS]; | |
54 | extern int debugger_is_slave[NCPUS]; | |
55 | extern int debugger_holdoff[NCPUS]; | |
56 | extern int debugger_sync; | |
57 | ||
58 | struct SIGtimebase { | |
59 | boolean_t avail; | |
60 | boolean_t ready; | |
61 | boolean_t done; | |
62 | AbsoluteTime abstime; | |
63 | }; | |
64 | ||
65 | extern struct SIGtimebase syncClkSpot; | |
66 | ||
67 | void cpu_sync_timebase(void); | |
68 | ||
69 | kern_return_t | |
70 | cpu_control( | |
71 | int slot_num, | |
72 | processor_info_t info, | |
73 | unsigned int count) | |
74 | { | |
75 | cpu_type_t cpu_type; | |
76 | cpu_subtype_t cpu_subtype; | |
77 | processor_pm_regs_t perf_regs; | |
78 | processor_control_cmd_t cmd; | |
79 | boolean_t oldlevel; | |
80 | ||
81 | cpu_type = machine_slot[slot_num].cpu_type; | |
82 | cpu_subtype = machine_slot[slot_num].cpu_subtype; | |
83 | cmd = (processor_control_cmd_t) info; | |
84 | ||
85 | if (count < PROCESSOR_CONTROL_CMD_COUNT) | |
86 | return(KERN_FAILURE); | |
87 | ||
88 | if ( cpu_type != cmd->cmd_cpu_type || | |
89 | cpu_subtype != cmd->cmd_cpu_subtype) | |
90 | return(KERN_FAILURE); | |
91 | ||
92 | switch (cmd->cmd_op) | |
93 | { | |
94 | case PROCESSOR_PM_CLR_PMC: /* Clear Performance Monitor Counters */ | |
95 | switch (cpu_subtype) | |
96 | { | |
97 | case CPU_SUBTYPE_POWERPC_604: | |
98 | { | |
99 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
100 | mtpmc1(0x0); | |
101 | mtpmc2(0x0); | |
102 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
103 | return(KERN_SUCCESS); | |
104 | } | |
105 | case CPU_SUBTYPE_POWERPC_604e: | |
106 | case CPU_SUBTYPE_POWERPC_750: | |
107 | case CPU_SUBTYPE_POWERPC_7400: | |
108 | case CPU_SUBTYPE_POWERPC_7450: | |
109 | { | |
110 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
111 | mtpmc1(0x0); | |
112 | mtpmc2(0x0); | |
113 | mtpmc3(0x0); | |
114 | mtpmc4(0x0); | |
115 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
116 | return(KERN_SUCCESS); | |
117 | } | |
118 | default: | |
119 | return(KERN_FAILURE); | |
120 | } /* cpu_subtype */ | |
121 | case PROCESSOR_PM_SET_REGS: /* Set Performance Monitor Registors */ | |
122 | switch (cpu_subtype) | |
123 | { | |
124 | case CPU_SUBTYPE_POWERPC_604: | |
125 | if (count < (PROCESSOR_CONTROL_CMD_COUNT | |
126 | + PROCESSOR_PM_REGS_COUNT_POWERPC_604)) | |
127 | return(KERN_FAILURE); | |
128 | else | |
129 | { | |
130 | perf_regs = (processor_pm_regs_t)cmd->cmd_pm_regs; | |
131 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
132 | mtmmcr0(PERFMON_MMCR0(perf_regs) & MMCR0_SUPPORT_MASK); | |
133 | mtpmc1(PERFMON_PMC1(perf_regs)); | |
134 | mtpmc2(PERFMON_PMC2(perf_regs)); | |
135 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
136 | return(KERN_SUCCESS); | |
137 | } | |
138 | case CPU_SUBTYPE_POWERPC_604e: | |
139 | case CPU_SUBTYPE_POWERPC_750: | |
140 | if (count < (PROCESSOR_CONTROL_CMD_COUNT + | |
141 | PROCESSOR_PM_REGS_COUNT_POWERPC_750)) | |
142 | return(KERN_FAILURE); | |
143 | else | |
144 | { | |
145 | perf_regs = (processor_pm_regs_t)cmd->cmd_pm_regs; | |
146 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
147 | mtmmcr0(PERFMON_MMCR0(perf_regs) & MMCR0_SUPPORT_MASK); | |
148 | mtpmc1(PERFMON_PMC1(perf_regs)); | |
149 | mtpmc2(PERFMON_PMC2(perf_regs)); | |
150 | mtmmcr1(PERFMON_MMCR1(perf_regs) & MMCR1_SUPPORT_MASK); | |
151 | mtpmc3(PERFMON_PMC3(perf_regs)); | |
152 | mtpmc4(PERFMON_PMC4(perf_regs)); | |
153 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
154 | return(KERN_SUCCESS); | |
155 | } | |
156 | case CPU_SUBTYPE_POWERPC_7400: | |
157 | case CPU_SUBTYPE_POWERPC_7450: | |
158 | if (count < (PROCESSOR_CONTROL_CMD_COUNT + | |
159 | PROCESSOR_PM_REGS_COUNT_POWERPC_7400)) | |
160 | return(KERN_FAILURE); | |
161 | else | |
162 | { | |
163 | perf_regs = (processor_pm_regs_t)cmd->cmd_pm_regs; | |
164 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
165 | mtmmcr0(PERFMON_MMCR0(perf_regs) & MMCR0_SUPPORT_MASK); | |
166 | mtpmc1(PERFMON_PMC1(perf_regs)); | |
167 | mtpmc2(PERFMON_PMC2(perf_regs)); | |
168 | mtmmcr1(PERFMON_MMCR1(perf_regs) & MMCR1_SUPPORT_MASK); | |
169 | mtpmc3(PERFMON_PMC3(perf_regs)); | |
170 | mtpmc4(PERFMON_PMC4(perf_regs)); | |
171 | mtmmcr2(PERFMON_MMCR2(perf_regs) & MMCR2_SUPPORT_MASK); | |
172 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
173 | return(KERN_SUCCESS); | |
174 | } | |
175 | default: | |
176 | return(KERN_FAILURE); | |
177 | } /* switch cpu_subtype */ | |
178 | case PROCESSOR_PM_SET_MMCR: | |
179 | switch (cpu_subtype) | |
180 | { | |
181 | case CPU_SUBTYPE_POWERPC_604: | |
182 | if (count < (PROCESSOR_CONTROL_CMD_COUNT + | |
183 | PROCESSOR_PM_REGS_COUNT_POWERPC_604)) | |
184 | return(KERN_FAILURE); | |
185 | else | |
186 | { | |
187 | perf_regs = (processor_pm_regs_t)cmd->cmd_pm_regs; | |
188 | mtmmcr0(PERFMON_MMCR0(perf_regs) & MMCR0_SUPPORT_MASK); | |
189 | return(KERN_SUCCESS); | |
190 | } | |
191 | case CPU_SUBTYPE_POWERPC_604e: | |
192 | case CPU_SUBTYPE_POWERPC_750: | |
193 | if (count < (PROCESSOR_CONTROL_CMD_COUNT + | |
194 | PROCESSOR_PM_REGS_COUNT_POWERPC_750)) | |
195 | return(KERN_FAILURE); | |
196 | else | |
197 | { | |
198 | perf_regs = (processor_pm_regs_t)cmd->cmd_pm_regs; | |
199 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
200 | mtmmcr0(PERFMON_MMCR0(perf_regs) & MMCR0_SUPPORT_MASK); | |
201 | mtmmcr1(PERFMON_MMCR1(perf_regs) & MMCR1_SUPPORT_MASK); | |
202 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
203 | return(KERN_SUCCESS); | |
204 | } | |
205 | case CPU_SUBTYPE_POWERPC_7400: | |
206 | case CPU_SUBTYPE_POWERPC_7450: | |
207 | if (count < (PROCESSOR_CONTROL_CMD_COUNT + | |
208 | PROCESSOR_PM_REGS_COUNT_POWERPC_7400)) | |
209 | return(KERN_FAILURE); | |
210 | else | |
211 | { | |
212 | perf_regs = (processor_pm_regs_t)cmd->cmd_pm_regs; | |
213 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
214 | mtmmcr0(PERFMON_MMCR0(perf_regs) & MMCR0_SUPPORT_MASK); | |
215 | mtmmcr1(PERFMON_MMCR1(perf_regs) & MMCR1_SUPPORT_MASK); | |
216 | mtmmcr2(PERFMON_MMCR2(perf_regs) & MMCR2_SUPPORT_MASK); | |
217 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
218 | return(KERN_SUCCESS); | |
219 | } | |
220 | default: | |
221 | return(KERN_FAILURE); | |
222 | } /* cpu_subtype */ | |
223 | default: | |
224 | return(KERN_FAILURE); | |
225 | } /* switch cmd_op */ | |
226 | } | |
227 | ||
228 | kern_return_t | |
229 | cpu_info_count( | |
230 | processor_flavor_t flavor, | |
231 | unsigned int *count) | |
232 | { | |
233 | cpu_subtype_t cpu_subtype; | |
234 | ||
235 | /* | |
236 | * For now, we just assume that all CPUs are of the same type | |
237 | */ | |
238 | cpu_subtype = machine_slot[0].cpu_subtype; | |
239 | switch (flavor) { | |
240 | case PROCESSOR_PM_REGS_INFO: | |
241 | switch (cpu_subtype) { | |
242 | case CPU_SUBTYPE_POWERPC_604: | |
243 | *count = PROCESSOR_PM_REGS_COUNT_POWERPC_604; | |
244 | return(KERN_SUCCESS); | |
245 | ||
246 | case CPU_SUBTYPE_POWERPC_604e: | |
247 | case CPU_SUBTYPE_POWERPC_750: | |
248 | ||
249 | *count = PROCESSOR_PM_REGS_COUNT_POWERPC_750; | |
250 | return(KERN_SUCCESS); | |
251 | ||
252 | case CPU_SUBTYPE_POWERPC_7400: | |
253 | case CPU_SUBTYPE_POWERPC_7450: | |
254 | ||
255 | *count = PROCESSOR_PM_REGS_COUNT_POWERPC_7400; | |
256 | return(KERN_SUCCESS); | |
257 | ||
258 | default: | |
259 | *count = 0; | |
260 | return(KERN_INVALID_ARGUMENT); | |
261 | } /* switch cpu_subtype */ | |
262 | ||
263 | case PROCESSOR_TEMPERATURE: | |
264 | *count = PROCESSOR_TEMPERATURE_COUNT; | |
265 | return (KERN_SUCCESS); | |
266 | ||
267 | default: | |
268 | *count = 0; | |
269 | return(KERN_INVALID_ARGUMENT); | |
270 | ||
271 | } | |
272 | } | |
273 | ||
274 | kern_return_t | |
275 | cpu_info( | |
276 | processor_flavor_t flavor, | |
277 | int slot_num, | |
278 | processor_info_t info, | |
279 | unsigned int *count) | |
280 | { | |
281 | cpu_subtype_t cpu_subtype; | |
282 | processor_pm_regs_t perf_regs; | |
283 | boolean_t oldlevel; | |
284 | unsigned int temp[2]; | |
285 | ||
286 | cpu_subtype = machine_slot[slot_num].cpu_subtype; | |
287 | ||
288 | switch (flavor) { | |
289 | case PROCESSOR_PM_REGS_INFO: | |
290 | ||
291 | perf_regs = (processor_pm_regs_t) info; | |
292 | ||
293 | switch (cpu_subtype) { | |
294 | case CPU_SUBTYPE_POWERPC_604: | |
295 | ||
296 | if (*count < PROCESSOR_PM_REGS_COUNT_POWERPC_604) | |
297 | return(KERN_FAILURE); | |
298 | ||
299 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
300 | PERFMON_MMCR0(perf_regs) = mfmmcr0(); | |
301 | PERFMON_PMC1(perf_regs) = mfpmc1(); | |
302 | PERFMON_PMC2(perf_regs) = mfpmc2(); | |
303 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
304 | ||
305 | *count = PROCESSOR_PM_REGS_COUNT_POWERPC_604; | |
306 | return(KERN_SUCCESS); | |
307 | ||
308 | case CPU_SUBTYPE_POWERPC_604e: | |
309 | case CPU_SUBTYPE_POWERPC_750: | |
310 | ||
311 | if (*count < PROCESSOR_PM_REGS_COUNT_POWERPC_750) | |
312 | return(KERN_FAILURE); | |
313 | ||
314 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
315 | PERFMON_MMCR0(perf_regs) = mfmmcr0(); | |
316 | PERFMON_PMC1(perf_regs) = mfpmc1(); | |
317 | PERFMON_PMC2(perf_regs) = mfpmc2(); | |
318 | PERFMON_MMCR1(perf_regs) = mfmmcr1(); | |
319 | PERFMON_PMC3(perf_regs) = mfpmc3(); | |
320 | PERFMON_PMC4(perf_regs) = mfpmc4(); | |
321 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
322 | ||
323 | *count = PROCESSOR_PM_REGS_COUNT_POWERPC_750; | |
324 | return(KERN_SUCCESS); | |
325 | ||
326 | case CPU_SUBTYPE_POWERPC_7400: | |
327 | case CPU_SUBTYPE_POWERPC_7450: | |
328 | ||
329 | if (*count < PROCESSOR_PM_REGS_COUNT_POWERPC_7400) | |
330 | return(KERN_FAILURE); | |
331 | ||
332 | oldlevel = ml_set_interrupts_enabled(FALSE); /* disable interrupts */ | |
333 | PERFMON_MMCR0(perf_regs) = mfmmcr0(); | |
334 | PERFMON_PMC1(perf_regs) = mfpmc1(); | |
335 | PERFMON_PMC2(perf_regs) = mfpmc2(); | |
336 | PERFMON_MMCR1(perf_regs) = mfmmcr1(); | |
337 | PERFMON_PMC3(perf_regs) = mfpmc3(); | |
338 | PERFMON_PMC4(perf_regs) = mfpmc4(); | |
339 | PERFMON_MMCR2(perf_regs) = mfmmcr2(); | |
340 | ml_set_interrupts_enabled(oldlevel); /* enable interrupts */ | |
341 | ||
342 | *count = PROCESSOR_PM_REGS_COUNT_POWERPC_7400; | |
343 | return(KERN_SUCCESS); | |
344 | ||
345 | default: | |
346 | return(KERN_FAILURE); | |
347 | } /* switch cpu_subtype */ | |
348 | ||
349 | case PROCESSOR_TEMPERATURE: /* Get the temperature of a processor */ | |
350 | ||
351 | disable_preemption(); /* Don't move me now */ | |
352 | ||
353 | if(slot_num == cpu_number()) { /* Is this for the local CPU? */ | |
354 | *info = ml_read_temp(); /* Get the temperature */ | |
355 | } | |
356 | else { /* For another CPU */ | |
357 | temp[0] = -1; /* Set sync flag */ | |
358 | eieio(); | |
359 | sync(); | |
360 | temp[1] = -1; /* Set invalid temperature */ | |
361 | (void)cpu_signal(slot_num, SIGPcpureq, CPRQtemp ,(unsigned int)&temp); /* Ask him to take his temperature */ | |
362 | (void)hw_cpu_sync(temp, LockTimeOut); /* Wait for the other processor to get its temperature */ | |
363 | *info = temp[1]; /* Pass it back */ | |
364 | } | |
365 | ||
366 | enable_preemption(); /* Ok to move now */ | |
367 | return(KERN_SUCCESS); | |
368 | ||
369 | default: | |
370 | return(KERN_INVALID_ARGUMENT); | |
371 | ||
372 | } /* flavor */ | |
373 | } | |
374 | ||
375 | void | |
376 | cpu_init( | |
377 | void) | |
378 | { | |
379 | int cpu; | |
380 | ||
381 | cpu = cpu_number(); | |
382 | ||
383 | machine_slot[cpu].running = TRUE; | |
384 | machine_slot[cpu].cpu_type = CPU_TYPE_POWERPC; | |
385 | machine_slot[cpu].cpu_subtype = (cpu_subtype_t)per_proc_info[cpu].pf.rptdProc; | |
386 | ||
387 | } | |
388 | ||
389 | void | |
390 | cpu_machine_init( | |
391 | void) | |
392 | { | |
393 | struct per_proc_info *proc_info; | |
394 | int cpu; | |
395 | ||
396 | /* TODO: realese mutex lock reset_handler_lock */ | |
397 | ||
398 | cpu = cpu_number(); | |
399 | proc_info = &per_proc_info[cpu]; | |
400 | PE_cpu_machine_init(proc_info->cpu_id, !(proc_info->cpu_flags & BootDone)); | |
401 | if (cpu != master_cpu) | |
402 | cpu_sync_timebase(); | |
403 | ml_init_interrupt(); | |
404 | proc_info->cpu_flags |= BootDone; | |
405 | } | |
406 | ||
407 | kern_return_t | |
408 | cpu_register( | |
409 | int *target_cpu | |
410 | ) | |
411 | { | |
412 | int cpu; | |
413 | ||
414 | /* | |
415 | * TODO: | |
416 | * - Run cpu_register() in exclusion mode | |
417 | */ | |
418 | ||
419 | *target_cpu = -1; | |
420 | for(cpu=0; cpu < wncpu; cpu++) { | |
421 | if(!machine_slot[cpu].is_cpu) { | |
422 | machine_slot[cpu].is_cpu = TRUE; | |
423 | *target_cpu = cpu; | |
424 | break; | |
425 | } | |
426 | } | |
427 | if (*target_cpu != -1) { | |
428 | real_ncpus++; | |
429 | return KERN_SUCCESS; | |
430 | } else | |
431 | return KERN_FAILURE; | |
432 | } | |
433 | ||
434 | kern_return_t | |
435 | cpu_start( | |
436 | int cpu) | |
437 | { | |
438 | struct per_proc_info *proc_info; | |
439 | kern_return_t ret; | |
440 | ||
441 | extern void (*exception_handlers[])(void); | |
442 | extern vm_offset_t intstack; | |
443 | extern vm_offset_t debstack; | |
444 | ||
445 | proc_info = &per_proc_info[cpu]; | |
446 | ||
447 | if (cpu == cpu_number()) { | |
448 | PE_cpu_machine_init(proc_info->cpu_id, !(proc_info->cpu_flags & BootDone)); | |
449 | ml_init_interrupt(); | |
450 | proc_info->cpu_flags |= BootDone; | |
451 | ||
452 | return KERN_SUCCESS; | |
453 | } else { | |
454 | extern void _start_cpu(void); | |
455 | ||
456 | proc_info->cpu_number = cpu; | |
457 | proc_info->cpu_flags &= BootDone; | |
458 | proc_info->istackptr = (vm_offset_t)&intstack + (INTSTACK_SIZE*(cpu+1)) - sizeof (struct ppc_saved_state); | |
459 | proc_info->intstack_top_ss = proc_info->istackptr; | |
460 | #if MACH_KDP || MACH_KDB | |
461 | proc_info->debstackptr = (vm_offset_t)&debstack + (KERNEL_STACK_SIZE*(cpu+1)) - sizeof (struct ppc_saved_state); | |
462 | proc_info->debstack_top_ss = proc_info->debstackptr; | |
463 | #endif /* MACH_KDP || MACH_KDB */ | |
464 | proc_info->get_interrupts_enabled = fake_get_interrupts_enabled; | |
465 | proc_info->set_interrupts_enabled = fake_set_interrupts_enabled; | |
466 | proc_info->active_kloaded = (unsigned int)&active_kloaded[cpu]; | |
467 | proc_info->cpu_data = (unsigned int)&cpu_data[cpu]; | |
468 | proc_info->active_stacks = (unsigned int)&active_stacks[cpu]; | |
469 | proc_info->need_ast = (unsigned int)&need_ast[cpu]; | |
470 | proc_info->FPU_thread = 0; | |
471 | proc_info->FPU_vmmCtx = 0; | |
472 | proc_info->VMX_thread = 0; | |
473 | proc_info->VMX_vmmCtx = 0; | |
474 | ||
475 | if (proc_info->start_paddr == EXCEPTION_VECTOR(T_RESET)) { | |
476 | ||
477 | /* TODO: get mutex lock reset_handler_lock */ | |
478 | ||
479 | resethandler_target.type = RESET_HANDLER_START; | |
480 | resethandler_target.call_paddr = kvtophys((vm_offset_t)_start_cpu); | |
481 | resethandler_target.arg__paddr = kvtophys((vm_offset_t)proc_info); | |
482 | ||
483 | ml_phys_write((vm_offset_t)&ResetHandler + 0, | |
484 | resethandler_target.type); | |
485 | ml_phys_write((vm_offset_t)&ResetHandler + 4, | |
486 | resethandler_target.call_paddr); | |
487 | ml_phys_write((vm_offset_t)&ResetHandler + 8, | |
488 | resethandler_target.arg__paddr); | |
489 | ||
490 | } | |
491 | /* | |
492 | * Note: we pass the current time to the other processor here. He will load it | |
493 | * as early as possible so that there is a chance that it is close to accurate. | |
494 | * After the machine is up a while, we will officially resync the clocks so | |
495 | * that all processors are the same. This is just to get close. | |
496 | */ | |
497 | ||
498 | ml_get_timebase(&proc_info->ruptStamp); /* Pass our current time to the other guy */ | |
499 | ||
500 | __asm__ volatile("sync"); /* Commit to storage */ | |
501 | __asm__ volatile("isync"); /* Wait a second */ | |
502 | ret = PE_cpu_start(proc_info->cpu_id, | |
503 | proc_info->start_paddr, (vm_offset_t)proc_info); | |
504 | ||
505 | if (ret != KERN_SUCCESS && | |
506 | proc_info->start_paddr == EXCEPTION_VECTOR(T_RESET)) { | |
507 | ||
508 | /* TODO: realese mutex lock reset_handler_lock */ | |
509 | } | |
510 | return(ret); | |
511 | } | |
512 | } | |
513 | ||
514 | /* | |
515 | * Here is where we implement the receiver of the signaling protocol. | |
516 | * We wait for the signal status area to be passed to us. Then we snarf | |
517 | * up the status, the sender, and the 3 potential parms. Next we release | |
518 | * the lock and signal the other guy. | |
519 | */ | |
520 | ||
521 | void | |
522 | cpu_signal_handler( | |
523 | void) | |
524 | { | |
525 | ||
526 | unsigned int holdStat, holdParm0, holdParm1, holdParm2, mtype; | |
527 | unsigned int *parmAddr; | |
528 | struct per_proc_info *pproc; /* Area for my per_proc address */ | |
529 | int cpu; | |
530 | struct SIGtimebase *timebaseAddr; | |
531 | natural_t tbu, tbu2, tbl; | |
532 | ||
533 | cpu = cpu_number(); /* Get the CPU number */ | |
534 | pproc = &per_proc_info[cpu]; /* Point to our block */ | |
535 | ||
536 | /* | |
537 | * Since we've been signaled, wait just under 1ms for the signal lock to pass | |
538 | */ | |
539 | if(!hw_lock_mbits(&pproc->MPsigpStat, MPsigpMsgp, (MPsigpBusy | MPsigpPass), | |
540 | (MPsigpBusy | MPsigpPass), (gPEClockFrequencyInfo.bus_clock_rate_hz >> 7))) { | |
541 | panic("cpu_signal_handler: Lock pass timed out\n"); | |
542 | } | |
543 | ||
544 | holdStat = pproc->MPsigpStat; /* Snarf stat word */ | |
545 | holdParm0 = pproc->MPsigpParm0; /* Snarf parameter */ | |
546 | holdParm1 = pproc->MPsigpParm1; /* Snarf parameter */ | |
547 | holdParm2 = pproc->MPsigpParm2; /* Snarf parameter */ | |
548 | ||
549 | __asm__ volatile("isync"); /* Make sure we don't unlock until memory is in */ | |
550 | ||
551 | pproc->MPsigpStat = holdStat & ~(MPsigpMsgp | MPsigpFunc); /* Release lock */ | |
552 | ||
553 | switch ((holdStat & MPsigpFunc) >> 8) { /* Decode function code */ | |
554 | ||
555 | case MPsigpIdle: /* Was function cancelled? */ | |
556 | return; /* Yup... */ | |
557 | ||
558 | case MPsigpSigp: /* Signal Processor message? */ | |
559 | ||
560 | switch (holdParm0) { /* Decode SIGP message order */ | |
561 | ||
562 | case SIGPast: /* Should we do an AST? */ | |
563 | pproc->numSIGPast++; /* Count this one */ | |
564 | #if 0 | |
565 | kprintf("cpu_signal_handler: AST check on cpu %x\n", cpu_number()); | |
566 | #endif | |
567 | ast_check(); /* Yes, do it */ | |
568 | /* XXX: Should check if AST_URGENT is needed */ | |
569 | ast_on(AST_URGENT); | |
570 | return; /* All done... */ | |
571 | ||
572 | case SIGPcpureq: /* CPU specific function? */ | |
573 | ||
574 | pproc->numSIGPcpureq++; /* Count this one */ | |
575 | switch (holdParm1) { /* Select specific function */ | |
576 | ||
577 | case CPRQtemp: /* Get the temperature */ | |
578 | parmAddr = (unsigned int *)holdParm2; /* Get the destination address */ | |
579 | parmAddr[1] = ml_read_temp(); /* Get the core temperature */ | |
580 | eieio(); /* Force order */ | |
581 | sync(); /* Force to memory */ | |
582 | parmAddr[0] = 0; /* Show we're done */ | |
583 | return; | |
584 | ||
585 | case CPRQtimebase: | |
586 | ||
587 | timebaseAddr = (struct SIGtimebase *)holdParm2; | |
588 | ||
589 | if(pproc->time_base_enable != (void(*)(cpu_id_t, boolean_t ))NULL) | |
590 | pproc->time_base_enable(pproc->cpu_id, FALSE); | |
591 | ||
592 | timebaseAddr->abstime.hi = 0; /* Touch to force into cache */ | |
593 | sync(); | |
594 | ||
595 | do { | |
596 | asm volatile(" mftbu %0" : "=r" (tbu)); | |
597 | asm volatile(" mftb %0" : "=r" (tbl)); | |
598 | asm volatile(" mftbu %0" : "=r" (tbu2)); | |
599 | } while (tbu != tbu2); | |
600 | ||
601 | timebaseAddr->abstime.lo = tbl; /* Set low order */ | |
602 | timebaseAddr->abstime.hi = tbu; /* Set high order */ | |
603 | sync(); /* Force order */ | |
604 | ||
605 | timebaseAddr->avail = TRUE; | |
606 | ||
607 | while (*(volatile int *)&(syncClkSpot.ready) == FALSE); | |
608 | ||
609 | if(pproc->time_base_enable != (void(*)(cpu_id_t, boolean_t ))NULL) | |
610 | pproc->time_base_enable(pproc->cpu_id, TRUE); | |
611 | ||
612 | timebaseAddr->done = TRUE; | |
613 | ||
614 | return; | |
615 | ||
616 | default: | |
617 | panic("cpu_signal_handler: unknown CPU request - %08X\n", holdParm1); | |
618 | return; | |
619 | } | |
620 | ||
621 | ||
622 | case SIGPdebug: /* Enter the debugger? */ | |
623 | ||
624 | pproc->numSIGPdebug++; /* Count this one */ | |
625 | debugger_is_slave[cpu]++; /* Bump up the count to show we're here */ | |
626 | hw_atomic_sub(&debugger_sync, 1); /* Show we've received the 'rupt */ | |
627 | __asm__ volatile("tw 4,r3,r3"); /* Enter the debugger */ | |
628 | return; /* All done now... */ | |
629 | ||
630 | case SIGPwake: /* Wake up CPU */ | |
631 | pproc->numSIGPwake++; /* Count this one */ | |
632 | return; /* No need to do anything, the interrupt does it all... */ | |
633 | ||
634 | default: | |
635 | panic("cpu_signal_handler: unknown SIGP message order - %08X\n", holdParm0); | |
636 | return; | |
637 | ||
638 | } | |
639 | ||
640 | default: | |
641 | panic("cpu_signal_handler: unknown SIGP function - %08X\n", (holdStat & MPsigpFunc) >> 8); | |
642 | return; | |
643 | ||
644 | } | |
645 | panic("cpu_signal_handler: we should never get here\n"); | |
646 | } | |
647 | ||
648 | /* | |
649 | * Here is where we send a message to another processor. So far we only have two: | |
650 | * SIGPast and SIGPdebug. SIGPast is used to preempt and kick off threads (this is | |
651 | * currently disabled). SIGPdebug is used to enter the debugger. | |
652 | * | |
653 | * We set up the SIGP function to indicate that this is a simple message and set the | |
654 | * order code (MPsigpParm0) to SIGPast or SIGPdebug). After finding the per_processor | |
655 | * block for the target, we lock the message block. Then we set the parameter(s). | |
656 | * Next we change the lock (also called "busy") to "passing" and finally signal | |
657 | * the other processor. Note that we only wait about 1ms to get the message lock. | |
658 | * If we time out, we return failure to our caller. It is their responsibility to | |
659 | * recover. | |
660 | */ | |
661 | ||
662 | kern_return_t | |
663 | cpu_signal( | |
664 | int target, | |
665 | int signal, | |
666 | unsigned int p1, | |
667 | unsigned int p2) | |
668 | { | |
669 | ||
670 | unsigned int holdStat, holdParm0, holdParm1, holdParm2, mtype; | |
671 | struct per_proc_info *tpproc, *mpproc; /* Area for per_proc addresses */ | |
672 | int cpu; | |
673 | ||
674 | #if DEBUG | |
675 | if(target > NCPUS) panic("cpu_signal: invalid target CPU - %08X\n", target); | |
676 | #endif | |
677 | ||
678 | cpu = cpu_number(); /* Get our CPU number */ | |
679 | if(target == cpu) return KERN_FAILURE; /* Don't play with ourselves */ | |
680 | if(!machine_slot[target].running) return KERN_FAILURE; /* These guys are too young */ | |
681 | ||
682 | mpproc = &per_proc_info[cpu]; /* Point to our block */ | |
683 | tpproc = &per_proc_info[target]; /* Point to the target's block */ | |
684 | ||
685 | if(!hw_lock_mbits(&tpproc->MPsigpStat, MPsigpMsgp, 0, MPsigpBusy, | |
686 | (gPEClockFrequencyInfo.bus_clock_rate_hz >> 7))) { /* Try to lock the message block */ | |
687 | return KERN_FAILURE; /* Timed out, take your ball and go home... */ | |
688 | } | |
689 | ||
690 | holdStat = MPsigpBusy | MPsigpPass | (MPsigpSigp << 8) | cpu; /* Set up the signal status word */ | |
691 | tpproc->MPsigpParm0 = signal; /* Set message order */ | |
692 | tpproc->MPsigpParm1 = p1; /* Set additional parm */ | |
693 | tpproc->MPsigpParm2 = p2; /* Set additional parm */ | |
694 | ||
695 | __asm__ volatile("sync"); /* Make sure it's all there */ | |
696 | ||
697 | tpproc->MPsigpStat = holdStat; /* Set status and pass the lock */ | |
698 | __asm__ volatile("eieio"); /* I'm a paraniod freak */ | |
699 | ||
700 | PE_cpu_signal(mpproc->cpu_id, tpproc->cpu_id); /* Kick the other processor */ | |
701 | ||
702 | return KERN_SUCCESS; /* All is goodness and rainbows... */ | |
703 | } | |
704 | ||
705 | void | |
706 | cpu_doshutdown( | |
707 | void) | |
708 | { | |
709 | processor_doshutdown(current_processor()); | |
710 | } | |
711 | ||
712 | void | |
713 | cpu_sleep( | |
714 | void) | |
715 | { | |
716 | struct per_proc_info *proc_info; | |
717 | unsigned int cpu; | |
718 | extern void (*exception_handlers[])(void); | |
719 | extern vm_offset_t intstack; | |
720 | extern vm_offset_t debstack; | |
721 | extern void _restart_cpu(void); | |
722 | ||
723 | cpu = cpu_number(); | |
724 | #if 0 | |
725 | kprintf("******* About to sleep cpu %d\n", cpu); | |
726 | #endif | |
727 | ||
728 | proc_info = &per_proc_info[cpu]; | |
729 | ||
730 | if (proc_info->cpu_number == 0) { | |
731 | proc_info->cpu_flags &= BootDone; | |
732 | proc_info->istackptr = (vm_offset_t)&intstack + (INTSTACK_SIZE*(cpu+1)) - sizeof (struct ppc_saved_state); | |
733 | proc_info->intstack_top_ss = proc_info->istackptr; | |
734 | #if MACH_KDP || MACH_KDB | |
735 | proc_info->debstackptr = (vm_offset_t)&debstack + (KERNEL_STACK_SIZE*(cpu+1)) - sizeof (struct ppc_saved_state); | |
736 | proc_info->debstack_top_ss = proc_info->debstackptr; | |
737 | #endif /* MACH_KDP || MACH_KDB */ | |
738 | proc_info->get_interrupts_enabled = fake_get_interrupts_enabled; | |
739 | proc_info->set_interrupts_enabled = fake_set_interrupts_enabled; | |
740 | proc_info->FPU_thread = 0; | |
741 | ||
742 | if (proc_info->start_paddr == EXCEPTION_VECTOR(T_RESET)) { | |
743 | extern void _start_cpu(void); | |
744 | ||
745 | resethandler_target.type = RESET_HANDLER_START; | |
746 | resethandler_target.call_paddr = kvtophys((vm_offset_t)_start_cpu); | |
747 | resethandler_target.arg__paddr = kvtophys((vm_offset_t)proc_info); | |
748 | ||
749 | ml_phys_write((vm_offset_t)&ResetHandler + 0, | |
750 | resethandler_target.type); | |
751 | ml_phys_write((vm_offset_t)&ResetHandler + 4, | |
752 | resethandler_target.call_paddr); | |
753 | ml_phys_write((vm_offset_t)&ResetHandler + 8, | |
754 | resethandler_target.arg__paddr); | |
755 | ||
756 | __asm__ volatile("sync"); | |
757 | __asm__ volatile("isync"); | |
758 | } | |
759 | } | |
760 | ||
761 | PE_cpu_machine_quiesce(proc_info->cpu_id); | |
762 | } | |
763 | ||
764 | void | |
765 | cpu_sync_timebase( | |
766 | void) | |
767 | { | |
768 | natural_t tbu, tbl; | |
769 | boolean_t intr; | |
770 | ||
771 | intr = ml_set_interrupts_enabled(FALSE); /* No interruptions in here */ | |
772 | ||
773 | /* Note that syncClkSpot is in a cache aligned area */ | |
774 | syncClkSpot.avail = FALSE; | |
775 | syncClkSpot.ready = FALSE; | |
776 | syncClkSpot.done = FALSE; | |
777 | ||
778 | while (cpu_signal(master_cpu, SIGPcpureq, CPRQtimebase, (unsigned int)&syncClkSpot) | |
779 | != KERN_SUCCESS); | |
780 | ||
781 | ||
782 | while (*(volatile int *)&(syncClkSpot.avail) == FALSE); | |
783 | isync(); | |
784 | ||
785 | /* | |
786 | * We do the following to keep the compiler from generating extra stuff | |
787 | * in tb set part | |
788 | */ | |
789 | tbu = syncClkSpot.abstime.hi; | |
790 | tbl = syncClkSpot.abstime.lo; | |
791 | ||
792 | mttb(0); | |
793 | mttbu(tbu); | |
794 | mttb(tbl); | |
795 | ||
796 | syncClkSpot.ready = TRUE; | |
797 | ||
798 | while (*(volatile int *)&(syncClkSpot.done) == FALSE); | |
799 | ||
800 | (void)ml_set_interrupts_enabled(intr); | |
801 | } |