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