Print this page
OS-2366 ddi_periodic_add(9F) is entirely rubbish
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/i86pc/io/pcplusmp/apic_common.c
+++ new/usr/src/uts/i86pc/io/pcplusmp/apic_common.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
↓ open down ↓ |
14 lines elided |
↑ open up ↑ |
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 24 */
25 +/*
26 + * Copyright (c) 2013, Joyent, Inc. All rights reserved.
27 + */
25 28
26 29 /*
27 30 * PSMI 1.1 extensions are supported only in 2.6 and later versions.
28 31 * PSMI 1.2 extensions are supported only in 2.7 and later versions.
29 32 * PSMI 1.3 and 1.4 extensions are supported in Solaris 10.
30 33 * PSMI 1.5 extensions are supported in Solaris Nevada.
31 34 * PSMI 1.6 extensions are supported in Solaris Nevada.
32 35 * PSMI 1.7 extensions are supported in Solaris Nevada.
33 36 */
34 37 #define PSMI_1_7
35 38
36 39 #include <sys/processor.h>
37 40 #include <sys/time.h>
38 41 #include <sys/psm.h>
39 42 #include <sys/smp_impldefs.h>
40 43 #include <sys/cram.h>
41 44 #include <sys/acpi/acpi.h>
42 45 #include <sys/acpica.h>
43 46 #include <sys/psm_common.h>
44 47 #include <sys/apic.h>
45 48 #include <sys/pit.h>
46 49 #include <sys/ddi.h>
47 50 #include <sys/sunddi.h>
48 51 #include <sys/ddi_impldefs.h>
49 52 #include <sys/pci.h>
50 53 #include <sys/promif.h>
51 54 #include <sys/x86_archext.h>
52 55 #include <sys/cpc_impl.h>
53 56 #include <sys/uadmin.h>
54 57 #include <sys/panic.h>
55 58 #include <sys/debug.h>
56 59 #include <sys/archsystm.h>
57 60 #include <sys/trap.h>
58 61 #include <sys/machsystm.h>
59 62 #include <sys/sysmacros.h>
60 63 #include <sys/cpuvar.h>
61 64 #include <sys/rm_platter.h>
62 65 #include <sys/privregs.h>
63 66 #include <sys/note.h>
64 67 #include <sys/pci_intr_lib.h>
65 68 #include <sys/spl.h>
66 69 #include <sys/clock.h>
67 70 #include <sys/dditypes.h>
68 71 #include <sys/sunddi.h>
69 72 #include <sys/x_call.h>
70 73 #include <sys/reboot.h>
71 74 #include <sys/hpet.h>
72 75 #include <sys/apic_common.h>
73 76 #include <sys/apic_timer.h>
74 77
75 78 static void apic_record_ioapic_rdt(void *intrmap_private,
76 79 ioapic_rdt_t *irdt);
77 80 static void apic_record_msi(void *intrmap_private, msi_regs_t *mregs);
78 81
79 82 /*
80 83 * Common routines between pcplusmp & apix (taken from apic.c).
81 84 */
82 85
83 86 int apic_clkinit(int);
84 87 hrtime_t apic_gethrtime(void);
85 88 void apic_send_ipi(int, int);
86 89 void apic_set_idlecpu(processorid_t);
87 90 void apic_unset_idlecpu(processorid_t);
88 91 void apic_shutdown(int, int);
89 92 void apic_preshutdown(int, int);
90 93 processorid_t apic_get_next_processorid(processorid_t);
91 94
92 95 hrtime_t apic_gettime();
93 96
94 97 enum apic_ioapic_method_type apix_mul_ioapic_method = APIC_MUL_IOAPIC_PCPLUSMP;
95 98
96 99 /* Now the ones for Dynamic Interrupt distribution */
97 100 int apic_enable_dynamic_migration = 0;
98 101
99 102 /* maximum loop count when sending Start IPIs. */
100 103 int apic_sipi_max_loop_count = 0x1000;
101 104
102 105 /*
103 106 * These variables are frequently accessed in apic_intr_enter(),
104 107 * apic_intr_exit and apic_setspl, so group them together
105 108 */
106 109 volatile uint32_t *apicadr = NULL; /* virtual addr of local APIC */
107 110 int apic_setspl_delay = 1; /* apic_setspl - delay enable */
108 111 int apic_clkvect;
109 112
110 113 /* vector at which error interrupts come in */
111 114 int apic_errvect;
112 115 int apic_enable_error_intr = 1;
113 116 int apic_error_display_delay = 100;
114 117
115 118 /* vector at which performance counter overflow interrupts come in */
116 119 int apic_cpcovf_vect;
117 120 int apic_enable_cpcovf_intr = 1;
118 121
119 122 /* vector at which CMCI interrupts come in */
120 123 int apic_cmci_vect;
121 124 extern int cmi_enable_cmci;
122 125 extern void cmi_cmci_trap(void);
123 126
124 127 kmutex_t cmci_cpu_setup_lock; /* protects cmci_cpu_setup_registered */
125 128 int cmci_cpu_setup_registered;
126 129
127 130 /* number of CPUs in power-on transition state */
128 131 static int apic_poweron_cnt = 0;
129 132 lock_t apic_mode_switch_lock;
130 133
131 134 /*
132 135 * Patchable global variables.
133 136 */
134 137 int apic_forceload = 0;
135 138
136 139 int apic_coarse_hrtime = 1; /* 0 - use accurate slow gethrtime() */
137 140
138 141 int apic_flat_model = 0; /* 0 - clustered. 1 - flat */
139 142 int apic_panic_on_nmi = 0;
140 143 int apic_panic_on_apic_error = 0;
141 144
142 145 int apic_verbose = 0; /* 0x1ff */
143 146
144 147 #ifdef DEBUG
145 148 int apic_debug = 0;
146 149 int apic_restrict_vector = 0;
147 150
148 151 int apic_debug_msgbuf[APIC_DEBUG_MSGBUFSIZE];
149 152 int apic_debug_msgbufindex = 0;
150 153
151 154 #endif /* DEBUG */
152 155
153 156 uint_t apic_nticks = 0;
154 157 uint_t apic_skipped_redistribute = 0;
155 158
156 159 uint_t last_count_read = 0;
157 160 lock_t apic_gethrtime_lock;
158 161 volatile int apic_hrtime_stamp = 0;
159 162 volatile hrtime_t apic_nsec_since_boot = 0;
160 163
161 164 static hrtime_t apic_last_hrtime = 0;
162 165 int apic_hrtime_error = 0;
163 166 int apic_remote_hrterr = 0;
164 167 int apic_num_nmis = 0;
165 168 int apic_apic_error = 0;
166 169 int apic_num_apic_errors = 0;
167 170 int apic_num_cksum_errors = 0;
168 171
169 172 int apic_error = 0;
170 173
171 174 static int apic_cmos_ssb_set = 0;
172 175
173 176 /* use to make sure only one cpu handles the nmi */
174 177 lock_t apic_nmi_lock;
175 178 /* use to make sure only one cpu handles the error interrupt */
176 179 lock_t apic_error_lock;
177 180
178 181 static struct {
179 182 uchar_t cntl;
180 183 uchar_t data;
181 184 } aspen_bmc[] = {
182 185 { CC_SMS_WR_START, 0x18 }, /* NetFn/LUN */
183 186 { CC_SMS_WR_NEXT, 0x24 }, /* Cmd SET_WATCHDOG_TIMER */
184 187 { CC_SMS_WR_NEXT, 0x84 }, /* DataByte 1: SMS/OS no log */
185 188 { CC_SMS_WR_NEXT, 0x2 }, /* DataByte 2: Power Down */
186 189 { CC_SMS_WR_NEXT, 0x0 }, /* DataByte 3: no pre-timeout */
187 190 { CC_SMS_WR_NEXT, 0x0 }, /* DataByte 4: timer expir. */
188 191 { CC_SMS_WR_NEXT, 0xa }, /* DataByte 5: init countdown */
189 192 { CC_SMS_WR_END, 0x0 }, /* DataByte 6: init countdown */
190 193
191 194 { CC_SMS_WR_START, 0x18 }, /* NetFn/LUN */
192 195 { CC_SMS_WR_END, 0x22 } /* Cmd RESET_WATCHDOG_TIMER */
193 196 };
194 197
195 198 static struct {
196 199 int port;
197 200 uchar_t data;
198 201 } sitka_bmc[] = {
199 202 { SMS_COMMAND_REGISTER, SMS_WRITE_START },
200 203 { SMS_DATA_REGISTER, 0x18 }, /* NetFn/LUN */
201 204 { SMS_DATA_REGISTER, 0x24 }, /* Cmd SET_WATCHDOG_TIMER */
202 205 { SMS_DATA_REGISTER, 0x84 }, /* DataByte 1: SMS/OS no log */
203 206 { SMS_DATA_REGISTER, 0x2 }, /* DataByte 2: Power Down */
204 207 { SMS_DATA_REGISTER, 0x0 }, /* DataByte 3: no pre-timeout */
205 208 { SMS_DATA_REGISTER, 0x0 }, /* DataByte 4: timer expir. */
206 209 { SMS_DATA_REGISTER, 0xa }, /* DataByte 5: init countdown */
207 210 { SMS_COMMAND_REGISTER, SMS_WRITE_END },
208 211 { SMS_DATA_REGISTER, 0x0 }, /* DataByte 6: init countdown */
209 212
210 213 { SMS_COMMAND_REGISTER, SMS_WRITE_START },
211 214 { SMS_DATA_REGISTER, 0x18 }, /* NetFn/LUN */
212 215 { SMS_COMMAND_REGISTER, SMS_WRITE_END },
213 216 { SMS_DATA_REGISTER, 0x22 } /* Cmd RESET_WATCHDOG_TIMER */
214 217 };
215 218
216 219 /* Patchable global variables. */
217 220 int apic_kmdb_on_nmi = 0; /* 0 - no, 1 - yes enter kmdb */
218 221 uint32_t apic_divide_reg_init = 0; /* 0 - divide by 2 */
219 222
220 223 /* default apic ops without interrupt remapping */
221 224 static apic_intrmap_ops_t apic_nointrmap_ops = {
222 225 (int (*)(int))return_instr,
223 226 (void (*)(int))return_instr,
224 227 (void (*)(void **, dev_info_t *, uint16_t, int, uchar_t))return_instr,
225 228 (void (*)(void *, void *, uint16_t, int))return_instr,
226 229 (void (*)(void **))return_instr,
227 230 apic_record_ioapic_rdt,
228 231 apic_record_msi,
229 232 };
230 233
231 234 apic_intrmap_ops_t *apic_vt_ops = &apic_nointrmap_ops;
232 235 apic_cpus_info_t *apic_cpus = NULL;
233 236 cpuset_t apic_cpumask;
234 237 uint_t apic_picinit_called;
235 238
236 239 /* Flag to indicate that we need to shut down all processors */
237 240 static uint_t apic_shutdown_processors;
238 241
239 242 /*
240 243 * Probe the ioapic method for apix module. Called in apic_probe_common()
241 244 */
242 245 int
243 246 apic_ioapic_method_probe()
244 247 {
245 248 if (apix_enable == 0)
246 249 return (PSM_SUCCESS);
247 250
248 251 /*
249 252 * Set IOAPIC EOI handling method. The priority from low to high is:
250 253 * 1. IOxAPIC: with EOI register
251 254 * 2. IOMMU interrupt mapping
252 255 * 3. Mask-Before-EOI method for systems without boot
253 256 * interrupt routing, such as systems with only one IOAPIC;
254 257 * NVIDIA CK8-04/MCP55 systems; systems with bridge solution
255 258 * which disables the boot interrupt routing already.
256 259 * 4. Directed EOI
257 260 */
258 261 if (apic_io_ver[0] >= 0x20)
259 262 apix_mul_ioapic_method = APIC_MUL_IOAPIC_IOXAPIC;
260 263 if ((apic_io_max == 1) || (apic_nvidia_io_max == apic_io_max))
261 264 apix_mul_ioapic_method = APIC_MUL_IOAPIC_MASK;
262 265 if (apic_directed_EOI_supported())
263 266 apix_mul_ioapic_method = APIC_MUL_IOAPIC_DEOI;
264 267
265 268 /* fall back to pcplusmp */
266 269 if (apix_mul_ioapic_method == APIC_MUL_IOAPIC_PCPLUSMP) {
267 270 /* make sure apix is after pcplusmp in /etc/mach */
268 271 apix_enable = 0; /* go ahead with pcplusmp install next */
269 272 return (PSM_FAILURE);
270 273 }
271 274
272 275 return (PSM_SUCCESS);
273 276 }
274 277
275 278 /*
276 279 * handler for APIC Error interrupt. Just print a warning and continue
277 280 */
278 281 int
279 282 apic_error_intr()
280 283 {
281 284 uint_t error0, error1, error;
282 285 uint_t i;
283 286
284 287 /*
285 288 * We need to write before read as per 7.4.17 of system prog manual.
286 289 * We do both and or the results to be safe
287 290 */
288 291 error0 = apic_reg_ops->apic_read(APIC_ERROR_STATUS);
289 292 apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0);
290 293 error1 = apic_reg_ops->apic_read(APIC_ERROR_STATUS);
291 294 error = error0 | error1;
292 295
293 296 /*
294 297 * Clear the APIC error status (do this on all cpus that enter here)
295 298 * (two writes are required due to the semantics of accessing the
296 299 * error status register.)
297 300 */
298 301 apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0);
299 302 apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0);
300 303
301 304 /*
302 305 * Prevent more than 1 CPU from handling error interrupt causing
303 306 * double printing (interleave of characters from multiple
304 307 * CPU's when using prom_printf)
305 308 */
306 309 if (lock_try(&apic_error_lock) == 0)
307 310 return (error ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
308 311 if (error) {
309 312 #if DEBUG
310 313 if (apic_debug)
311 314 debug_enter("pcplusmp: APIC Error interrupt received");
312 315 #endif /* DEBUG */
313 316 if (apic_panic_on_apic_error)
314 317 cmn_err(CE_PANIC,
315 318 "APIC Error interrupt on CPU %d. Status = %x",
316 319 psm_get_cpu_id(), error);
317 320 else {
318 321 if ((error & ~APIC_CS_ERRORS) == 0) {
319 322 /* cksum error only */
320 323 apic_error |= APIC_ERR_APIC_ERROR;
321 324 apic_apic_error |= error;
322 325 apic_num_apic_errors++;
323 326 apic_num_cksum_errors++;
324 327 } else {
325 328 /*
326 329 * prom_printf is the best shot we have of
327 330 * something which is problem free from
328 331 * high level/NMI type of interrupts
329 332 */
330 333 prom_printf("APIC Error interrupt on CPU %d. "
331 334 "Status 0 = %x, Status 1 = %x\n",
332 335 psm_get_cpu_id(), error0, error1);
333 336 apic_error |= APIC_ERR_APIC_ERROR;
334 337 apic_apic_error |= error;
335 338 apic_num_apic_errors++;
336 339 for (i = 0; i < apic_error_display_delay; i++) {
337 340 tenmicrosec();
338 341 }
339 342 /*
340 343 * provide more delay next time limited to
341 344 * roughly 1 clock tick time
342 345 */
343 346 if (apic_error_display_delay < 500)
344 347 apic_error_display_delay *= 2;
345 348 }
346 349 }
347 350 lock_clear(&apic_error_lock);
348 351 return (DDI_INTR_CLAIMED);
349 352 } else {
350 353 lock_clear(&apic_error_lock);
351 354 return (DDI_INTR_UNCLAIMED);
352 355 }
353 356 }
354 357
355 358 /*
356 359 * Turn off the mask bit in the performance counter Local Vector Table entry.
357 360 */
358 361 void
359 362 apic_cpcovf_mask_clear(void)
360 363 {
361 364 apic_reg_ops->apic_write(APIC_PCINT_VECT,
362 365 (apic_reg_ops->apic_read(APIC_PCINT_VECT) & ~APIC_LVT_MASK));
363 366 }
364 367
365 368 /*ARGSUSED*/
366 369 static int
367 370 apic_cmci_enable(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3)
368 371 {
369 372 apic_reg_ops->apic_write(APIC_CMCI_VECT, apic_cmci_vect);
370 373 return (0);
371 374 }
372 375
373 376 /*ARGSUSED*/
374 377 static int
375 378 apic_cmci_disable(xc_arg_t arg1, xc_arg_t arg2, xc_arg_t arg3)
376 379 {
377 380 apic_reg_ops->apic_write(APIC_CMCI_VECT, apic_cmci_vect | AV_MASK);
378 381 return (0);
379 382 }
380 383
381 384 /*ARGSUSED*/
382 385 int
383 386 cmci_cpu_setup(cpu_setup_t what, int cpuid, void *arg)
384 387 {
385 388 cpuset_t cpu_set;
386 389
387 390 CPUSET_ONLY(cpu_set, cpuid);
388 391
389 392 switch (what) {
390 393 case CPU_ON:
391 394 xc_call(NULL, NULL, NULL, CPUSET2BV(cpu_set),
392 395 (xc_func_t)apic_cmci_enable);
393 396 break;
394 397
395 398 case CPU_OFF:
396 399 xc_call(NULL, NULL, NULL, CPUSET2BV(cpu_set),
397 400 (xc_func_t)apic_cmci_disable);
398 401 break;
399 402
400 403 default:
401 404 break;
402 405 }
403 406
404 407 return (0);
405 408 }
406 409
407 410 static void
408 411 apic_disable_local_apic(void)
409 412 {
410 413 apic_reg_ops->apic_write_task_reg(APIC_MASK_ALL);
411 414 apic_reg_ops->apic_write(APIC_LOCAL_TIMER, AV_MASK);
412 415
413 416 /* local intr reg 0 */
414 417 apic_reg_ops->apic_write(APIC_INT_VECT0, AV_MASK);
415 418
416 419 /* disable NMI */
417 420 apic_reg_ops->apic_write(APIC_INT_VECT1, AV_MASK);
418 421
419 422 /* and error interrupt */
420 423 apic_reg_ops->apic_write(APIC_ERR_VECT, AV_MASK);
421 424
422 425 /* and perf counter intr */
423 426 apic_reg_ops->apic_write(APIC_PCINT_VECT, AV_MASK);
424 427
425 428 apic_reg_ops->apic_write(APIC_SPUR_INT_REG, APIC_SPUR_INTR);
426 429 }
427 430
428 431 static void
429 432 apic_cpu_send_SIPI(processorid_t cpun, boolean_t start)
430 433 {
431 434 int loop_count;
432 435 uint32_t vector;
433 436 uint_t apicid;
434 437 ulong_t iflag;
435 438
436 439 apicid = apic_cpus[cpun].aci_local_id;
437 440
438 441 /*
439 442 * Interrupts on current CPU will be disabled during the
440 443 * steps in order to avoid unwanted side effects from
441 444 * executing interrupt handlers on a problematic BIOS.
442 445 */
443 446 iflag = intr_clear();
444 447
445 448 if (start) {
446 449 outb(CMOS_ADDR, SSB);
447 450 outb(CMOS_DATA, BIOS_SHUTDOWN);
448 451 }
449 452
450 453 /*
451 454 * According to X2APIC specification in section '2.3.5.1' of
452 455 * Interrupt Command Register Semantics, the semantics of
453 456 * programming the Interrupt Command Register to dispatch an interrupt
454 457 * is simplified. A single MSR write to the 64-bit ICR is required
455 458 * for dispatching an interrupt. Specifically, with the 64-bit MSR
456 459 * interface to ICR, system software is not required to check the
457 460 * status of the delivery status bit prior to writing to the ICR
458 461 * to send an IPI. With the removal of the Delivery Status bit,
459 462 * system software no longer has a reason to read the ICR. It remains
460 463 * readable only to aid in debugging.
461 464 */
462 465 #ifdef DEBUG
463 466 APIC_AV_PENDING_SET();
464 467 #else
465 468 if (apic_mode == LOCAL_APIC) {
466 469 APIC_AV_PENDING_SET();
467 470 }
468 471 #endif /* DEBUG */
469 472
470 473 /* for integrated - make sure there is one INIT IPI in buffer */
471 474 /* for external - it will wake up the cpu */
472 475 apic_reg_ops->apic_write_int_cmd(apicid, AV_ASSERT | AV_RESET);
473 476
474 477 /* If only 1 CPU is installed, PENDING bit will not go low */
475 478 for (loop_count = apic_sipi_max_loop_count; loop_count; loop_count--) {
476 479 if (apic_mode == LOCAL_APIC &&
477 480 apic_reg_ops->apic_read(APIC_INT_CMD1) & AV_PENDING)
478 481 apic_ret();
479 482 else
480 483 break;
481 484 }
482 485
483 486 apic_reg_ops->apic_write_int_cmd(apicid, AV_DEASSERT | AV_RESET);
484 487 drv_usecwait(20000); /* 20 milli sec */
485 488
486 489 if (apic_cpus[cpun].aci_local_ver >= APIC_INTEGRATED_VERS) {
487 490 /* integrated apic */
488 491
489 492 vector = (rm_platter_pa >> MMU_PAGESHIFT) &
490 493 (APIC_VECTOR_MASK | APIC_IPL_MASK);
491 494
492 495 /* to offset the INIT IPI queue up in the buffer */
493 496 apic_reg_ops->apic_write_int_cmd(apicid, vector | AV_STARTUP);
494 497 drv_usecwait(200); /* 20 micro sec */
495 498
496 499 /*
497 500 * send the second SIPI (Startup IPI) as recommended by Intel
498 501 * software development manual.
499 502 */
500 503 apic_reg_ops->apic_write_int_cmd(apicid, vector | AV_STARTUP);
501 504 drv_usecwait(200); /* 20 micro sec */
502 505 }
503 506
504 507 intr_restore(iflag);
505 508 }
506 509
507 510 /*ARGSUSED1*/
508 511 int
509 512 apic_cpu_start(processorid_t cpun, caddr_t arg)
510 513 {
511 514 ASSERT(MUTEX_HELD(&cpu_lock));
512 515
513 516 if (!apic_cpu_in_range(cpun)) {
514 517 return (EINVAL);
515 518 }
516 519
517 520 /*
518 521 * Switch to apic_common_send_ipi for safety during starting other CPUs.
519 522 */
520 523 if (apic_mode == LOCAL_X2APIC) {
521 524 apic_switch_ipi_callback(B_TRUE);
522 525 }
523 526
524 527 apic_cmos_ssb_set = 1;
525 528 apic_cpu_send_SIPI(cpun, B_TRUE);
526 529
527 530 return (0);
528 531 }
529 532
530 533 /*
531 534 * Put CPU into halted state with interrupts disabled.
532 535 */
533 536 /*ARGSUSED1*/
534 537 int
535 538 apic_cpu_stop(processorid_t cpun, caddr_t arg)
536 539 {
537 540 int rc;
538 541 cpu_t *cp;
539 542 extern cpuset_t cpu_ready_set;
540 543 extern void cpu_idle_intercept_cpu(cpu_t *cp);
541 544
542 545 ASSERT(MUTEX_HELD(&cpu_lock));
543 546
544 547 if (!apic_cpu_in_range(cpun)) {
545 548 return (EINVAL);
546 549 }
547 550 if (apic_cpus[cpun].aci_local_ver < APIC_INTEGRATED_VERS) {
548 551 return (ENOTSUP);
549 552 }
550 553
551 554 cp = cpu_get(cpun);
552 555 ASSERT(cp != NULL);
553 556 ASSERT((cp->cpu_flags & CPU_OFFLINE) != 0);
554 557 ASSERT((cp->cpu_flags & CPU_QUIESCED) != 0);
555 558 ASSERT((cp->cpu_flags & CPU_ENABLE) == 0);
556 559
557 560 /* Clear CPU_READY flag to disable cross calls. */
558 561 cp->cpu_flags &= ~CPU_READY;
559 562 CPUSET_ATOMIC_DEL(cpu_ready_set, cpun);
560 563 rc = xc_flush_cpu(cp);
561 564 if (rc != 0) {
562 565 CPUSET_ATOMIC_ADD(cpu_ready_set, cpun);
563 566 cp->cpu_flags |= CPU_READY;
564 567 return (rc);
565 568 }
566 569
567 570 /* Intercept target CPU at a safe point before powering it off. */
568 571 cpu_idle_intercept_cpu(cp);
569 572
570 573 apic_cpu_send_SIPI(cpun, B_FALSE);
571 574 cp->cpu_flags &= ~CPU_RUNNING;
572 575
573 576 return (0);
574 577 }
575 578
576 579 int
577 580 apic_cpu_ops(psm_cpu_request_t *reqp)
578 581 {
579 582 if (reqp == NULL) {
580 583 return (EINVAL);
581 584 }
582 585
583 586 switch (reqp->pcr_cmd) {
584 587 case PSM_CPU_ADD:
585 588 return (apic_cpu_add(reqp));
586 589
587 590 case PSM_CPU_REMOVE:
588 591 return (apic_cpu_remove(reqp));
589 592
590 593 case PSM_CPU_STOP:
591 594 return (apic_cpu_stop(reqp->req.cpu_stop.cpuid,
592 595 reqp->req.cpu_stop.ctx));
593 596
594 597 default:
595 598 return (ENOTSUP);
596 599 }
597 600 }
598 601
599 602 #ifdef DEBUG
600 603 int apic_break_on_cpu = 9;
601 604 int apic_stretch_interrupts = 0;
602 605 int apic_stretch_ISR = 1 << 3; /* IPL of 3 matches nothing now */
603 606 #endif /* DEBUG */
604 607
605 608 /*
606 609 * generates an interprocessor interrupt to another CPU. Any changes made to
607 610 * this routine must be accompanied by similar changes to
608 611 * apic_common_send_ipi().
609 612 */
610 613 void
611 614 apic_send_ipi(int cpun, int ipl)
612 615 {
613 616 int vector;
614 617 ulong_t flag;
615 618
616 619 vector = apic_resv_vector[ipl];
617 620
618 621 ASSERT((vector >= APIC_BASE_VECT) && (vector <= APIC_SPUR_INTR));
619 622
620 623 flag = intr_clear();
621 624
622 625 APIC_AV_PENDING_SET();
623 626
624 627 apic_reg_ops->apic_write_int_cmd(apic_cpus[cpun].aci_local_id,
625 628 vector);
626 629
627 630 intr_restore(flag);
628 631 }
629 632
630 633
631 634 /*ARGSUSED*/
632 635 void
633 636 apic_set_idlecpu(processorid_t cpun)
634 637 {
635 638 }
636 639
637 640 /*ARGSUSED*/
638 641 void
639 642 apic_unset_idlecpu(processorid_t cpun)
640 643 {
641 644 }
642 645
643 646
644 647 void
645 648 apic_ret()
646 649 {
647 650 }
648 651
649 652 /*
650 653 * If apic_coarse_time == 1, then apic_gettime() is used instead of
651 654 * apic_gethrtime(). This is used for performance instead of accuracy.
652 655 */
653 656
654 657 hrtime_t
655 658 apic_gettime()
656 659 {
657 660 int old_hrtime_stamp;
658 661 hrtime_t temp;
659 662
660 663 /*
661 664 * In one-shot mode, we do not keep time, so if anyone
662 665 * calls psm_gettime() directly, we vector over to
663 666 * gethrtime().
664 667 * one-shot mode MUST NOT be enabled if this psm is the source of
665 668 * hrtime.
666 669 */
667 670
668 671 if (apic_oneshot)
669 672 return (gethrtime());
670 673
671 674
672 675 gettime_again:
673 676 while ((old_hrtime_stamp = apic_hrtime_stamp) & 1)
674 677 apic_ret();
675 678
676 679 temp = apic_nsec_since_boot;
677 680
678 681 if (apic_hrtime_stamp != old_hrtime_stamp) { /* got an interrupt */
679 682 goto gettime_again;
680 683 }
681 684 return (temp);
682 685 }
683 686
684 687 /*
685 688 * Here we return the number of nanoseconds since booting. Note every
686 689 * clock interrupt increments apic_nsec_since_boot by the appropriate
687 690 * amount.
688 691 */
689 692 hrtime_t
690 693 apic_gethrtime(void)
691 694 {
692 695 int curr_timeval, countval, elapsed_ticks;
693 696 int old_hrtime_stamp, status;
694 697 hrtime_t temp;
695 698 uint32_t cpun;
696 699 ulong_t oflags;
697 700
698 701 /*
699 702 * In one-shot mode, we do not keep time, so if anyone
700 703 * calls psm_gethrtime() directly, we vector over to
701 704 * gethrtime().
702 705 * one-shot mode MUST NOT be enabled if this psm is the source of
703 706 * hrtime.
704 707 */
705 708
706 709 if (apic_oneshot)
707 710 return (gethrtime());
708 711
709 712 oflags = intr_clear(); /* prevent migration */
710 713
711 714 cpun = apic_reg_ops->apic_read(APIC_LID_REG);
712 715 if (apic_mode == LOCAL_APIC)
713 716 cpun >>= APIC_ID_BIT_OFFSET;
714 717
715 718 lock_set(&apic_gethrtime_lock);
716 719
717 720 gethrtime_again:
718 721 while ((old_hrtime_stamp = apic_hrtime_stamp) & 1)
719 722 apic_ret();
720 723
721 724 /*
722 725 * Check to see which CPU we are on. Note the time is kept on
723 726 * the local APIC of CPU 0. If on CPU 0, simply read the current
724 727 * counter. If on another CPU, issue a remote read command to CPU 0.
725 728 */
726 729 if (cpun == apic_cpus[0].aci_local_id) {
727 730 countval = apic_reg_ops->apic_read(APIC_CURR_COUNT);
728 731 } else {
729 732 #ifdef DEBUG
730 733 APIC_AV_PENDING_SET();
731 734 #else
732 735 if (apic_mode == LOCAL_APIC)
733 736 APIC_AV_PENDING_SET();
734 737 #endif /* DEBUG */
735 738
736 739 apic_reg_ops->apic_write_int_cmd(
737 740 apic_cpus[0].aci_local_id, APIC_CURR_ADD | AV_REMOTE);
738 741
739 742 while ((status = apic_reg_ops->apic_read(APIC_INT_CMD1))
740 743 & AV_READ_PENDING) {
741 744 apic_ret();
742 745 }
743 746
744 747 if (status & AV_REMOTE_STATUS) /* 1 = valid */
745 748 countval = apic_reg_ops->apic_read(APIC_REMOTE_READ);
746 749 else { /* 0 = invalid */
747 750 apic_remote_hrterr++;
748 751 /*
749 752 * return last hrtime right now, will need more
750 753 * testing if change to retry
751 754 */
752 755 temp = apic_last_hrtime;
753 756
754 757 lock_clear(&apic_gethrtime_lock);
755 758
756 759 intr_restore(oflags);
757 760
758 761 return (temp);
759 762 }
760 763 }
761 764 if (countval > last_count_read)
762 765 countval = 0;
763 766 else
764 767 last_count_read = countval;
765 768
766 769 elapsed_ticks = apic_hertz_count - countval;
767 770
768 771 curr_timeval = APIC_TICKS_TO_NSECS(elapsed_ticks);
769 772 temp = apic_nsec_since_boot + curr_timeval;
770 773
771 774 if (apic_hrtime_stamp != old_hrtime_stamp) { /* got an interrupt */
772 775 /* we might have clobbered last_count_read. Restore it */
773 776 last_count_read = apic_hertz_count;
774 777 goto gethrtime_again;
775 778 }
776 779
777 780 if (temp < apic_last_hrtime) {
778 781 /* return last hrtime if error occurs */
779 782 apic_hrtime_error++;
780 783 temp = apic_last_hrtime;
781 784 }
782 785 else
783 786 apic_last_hrtime = temp;
784 787
785 788 lock_clear(&apic_gethrtime_lock);
786 789 intr_restore(oflags);
787 790
788 791 return (temp);
789 792 }
790 793
791 794 /* apic NMI handler */
792 795 /*ARGSUSED*/
793 796 void
794 797 apic_nmi_intr(caddr_t arg, struct regs *rp)
795 798 {
796 799 if (apic_shutdown_processors) {
797 800 apic_disable_local_apic();
798 801 return;
799 802 }
800 803
801 804 apic_error |= APIC_ERR_NMI;
802 805
803 806 if (!lock_try(&apic_nmi_lock))
804 807 return;
805 808 apic_num_nmis++;
806 809
807 810 if (apic_kmdb_on_nmi && psm_debugger()) {
808 811 debug_enter("NMI received: entering kmdb\n");
809 812 } else if (apic_panic_on_nmi) {
810 813 /* Keep panic from entering kmdb. */
811 814 nopanicdebug = 1;
812 815 panic("NMI received\n");
813 816 } else {
814 817 /*
815 818 * prom_printf is the best shot we have of something which is
816 819 * problem free from high level/NMI type of interrupts
817 820 */
818 821 prom_printf("NMI received\n");
819 822 }
820 823
821 824 lock_clear(&apic_nmi_lock);
822 825 }
823 826
824 827 processorid_t
825 828 apic_get_next_processorid(processorid_t cpu_id)
826 829 {
827 830
828 831 int i;
829 832
830 833 if (cpu_id == -1)
831 834 return ((processorid_t)0);
832 835
833 836 for (i = cpu_id + 1; i < NCPU; i++) {
834 837 if (apic_cpu_in_range(i))
835 838 return (i);
836 839 }
837 840
838 841 return ((processorid_t)-1);
839 842 }
840 843
841 844 int
842 845 apic_cpu_add(psm_cpu_request_t *reqp)
843 846 {
844 847 int i, rv = 0;
845 848 ulong_t iflag;
846 849 boolean_t first = B_TRUE;
847 850 uchar_t localver;
848 851 uint32_t localid, procid;
849 852 processorid_t cpuid = (processorid_t)-1;
850 853 mach_cpu_add_arg_t *ap;
851 854
852 855 ASSERT(reqp != NULL);
853 856 reqp->req.cpu_add.cpuid = (processorid_t)-1;
854 857
855 858 /* Check whether CPU hotplug is supported. */
856 859 if (!plat_dr_support_cpu() || apic_max_nproc == -1) {
857 860 return (ENOTSUP);
858 861 }
859 862
860 863 ap = (mach_cpu_add_arg_t *)reqp->req.cpu_add.argp;
861 864 switch (ap->type) {
862 865 case MACH_CPU_ARG_LOCAL_APIC:
863 866 localid = ap->arg.apic.apic_id;
864 867 procid = ap->arg.apic.proc_id;
865 868 if (localid >= 255 || procid > 255) {
866 869 cmn_err(CE_WARN,
867 870 "!apic: apicid(%u) or procid(%u) is invalid.",
868 871 localid, procid);
869 872 return (EINVAL);
870 873 }
871 874 break;
872 875
873 876 case MACH_CPU_ARG_LOCAL_X2APIC:
874 877 localid = ap->arg.apic.apic_id;
875 878 procid = ap->arg.apic.proc_id;
876 879 if (localid >= UINT32_MAX) {
877 880 cmn_err(CE_WARN,
878 881 "!apic: x2apicid(%u) is invalid.", localid);
879 882 return (EINVAL);
880 883 } else if (localid >= 255 && apic_mode == LOCAL_APIC) {
881 884 cmn_err(CE_WARN, "!apic: system is in APIC mode, "
882 885 "can't support x2APIC processor.");
883 886 return (ENOTSUP);
884 887 }
885 888 break;
886 889
887 890 default:
888 891 cmn_err(CE_WARN,
889 892 "!apic: unknown argument type %d to apic_cpu_add().",
890 893 ap->type);
891 894 return (EINVAL);
892 895 }
893 896
894 897 /* Use apic_ioapic_lock to sync with apic_get_next_bind_cpu. */
895 898 iflag = intr_clear();
896 899 lock_set(&apic_ioapic_lock);
897 900
898 901 /* Check whether local APIC id already exists. */
899 902 for (i = 0; i < apic_nproc; i++) {
900 903 if (!CPU_IN_SET(apic_cpumask, i))
901 904 continue;
902 905 if (apic_cpus[i].aci_local_id == localid) {
903 906 lock_clear(&apic_ioapic_lock);
904 907 intr_restore(iflag);
905 908 cmn_err(CE_WARN,
906 909 "!apic: local apic id %u already exists.",
907 910 localid);
908 911 return (EEXIST);
909 912 } else if (apic_cpus[i].aci_processor_id == procid) {
910 913 lock_clear(&apic_ioapic_lock);
911 914 intr_restore(iflag);
912 915 cmn_err(CE_WARN,
913 916 "!apic: processor id %u already exists.",
914 917 (int)procid);
915 918 return (EEXIST);
916 919 }
917 920
918 921 /*
919 922 * There's no local APIC version number available in MADT table,
920 923 * so assume that all CPUs are homogeneous and use local APIC
921 924 * version number of the first existing CPU.
922 925 */
923 926 if (first) {
924 927 first = B_FALSE;
925 928 localver = apic_cpus[i].aci_local_ver;
926 929 }
927 930 }
928 931 ASSERT(first == B_FALSE);
929 932
930 933 /*
931 934 * Try to assign the same cpuid if APIC id exists in the dirty cache.
932 935 */
933 936 for (i = 0; i < apic_max_nproc; i++) {
934 937 if (CPU_IN_SET(apic_cpumask, i)) {
935 938 ASSERT((apic_cpus[i].aci_status & APIC_CPU_FREE) == 0);
936 939 continue;
937 940 }
938 941 ASSERT(apic_cpus[i].aci_status & APIC_CPU_FREE);
939 942 if ((apic_cpus[i].aci_status & APIC_CPU_DIRTY) &&
940 943 apic_cpus[i].aci_local_id == localid &&
941 944 apic_cpus[i].aci_processor_id == procid) {
942 945 cpuid = i;
943 946 break;
944 947 }
945 948 }
946 949
947 950 /* Avoid the dirty cache and allocate fresh slot if possible. */
948 951 if (cpuid == (processorid_t)-1) {
949 952 for (i = 0; i < apic_max_nproc; i++) {
950 953 if ((apic_cpus[i].aci_status & APIC_CPU_FREE) &&
951 954 (apic_cpus[i].aci_status & APIC_CPU_DIRTY) == 0) {
952 955 cpuid = i;
953 956 break;
954 957 }
955 958 }
956 959 }
957 960
958 961 /* Try to find any free slot as last resort. */
959 962 if (cpuid == (processorid_t)-1) {
960 963 for (i = 0; i < apic_max_nproc; i++) {
961 964 if (apic_cpus[i].aci_status & APIC_CPU_FREE) {
962 965 cpuid = i;
963 966 break;
964 967 }
965 968 }
966 969 }
967 970
968 971 if (cpuid == (processorid_t)-1) {
969 972 lock_clear(&apic_ioapic_lock);
970 973 intr_restore(iflag);
971 974 cmn_err(CE_NOTE,
972 975 "!apic: failed to allocate cpu id for processor %u.",
973 976 procid);
974 977 rv = EAGAIN;
975 978 } else if (ACPI_FAILURE(acpica_map_cpu(cpuid, procid))) {
976 979 lock_clear(&apic_ioapic_lock);
977 980 intr_restore(iflag);
978 981 cmn_err(CE_NOTE,
979 982 "!apic: failed to build mapping for processor %u.",
980 983 procid);
981 984 rv = EBUSY;
982 985 } else {
983 986 ASSERT(cpuid >= 0 && cpuid < NCPU);
984 987 ASSERT(cpuid < apic_max_nproc && cpuid < max_ncpus);
985 988 bzero(&apic_cpus[cpuid], sizeof (apic_cpus[0]));
986 989 apic_cpus[cpuid].aci_processor_id = procid;
987 990 apic_cpus[cpuid].aci_local_id = localid;
988 991 apic_cpus[cpuid].aci_local_ver = localver;
989 992 CPUSET_ATOMIC_ADD(apic_cpumask, cpuid);
990 993 if (cpuid >= apic_nproc) {
991 994 apic_nproc = cpuid + 1;
992 995 }
993 996 lock_clear(&apic_ioapic_lock);
994 997 intr_restore(iflag);
995 998 reqp->req.cpu_add.cpuid = cpuid;
996 999 }
997 1000
998 1001 return (rv);
999 1002 }
1000 1003
1001 1004 int
1002 1005 apic_cpu_remove(psm_cpu_request_t *reqp)
1003 1006 {
1004 1007 int i;
1005 1008 ulong_t iflag;
1006 1009 processorid_t cpuid;
1007 1010
1008 1011 /* Check whether CPU hotplug is supported. */
1009 1012 if (!plat_dr_support_cpu() || apic_max_nproc == -1) {
1010 1013 return (ENOTSUP);
1011 1014 }
1012 1015
1013 1016 cpuid = reqp->req.cpu_remove.cpuid;
1014 1017
1015 1018 /* Use apic_ioapic_lock to sync with apic_get_next_bind_cpu. */
1016 1019 iflag = intr_clear();
1017 1020 lock_set(&apic_ioapic_lock);
1018 1021
1019 1022 if (!apic_cpu_in_range(cpuid)) {
1020 1023 lock_clear(&apic_ioapic_lock);
1021 1024 intr_restore(iflag);
1022 1025 cmn_err(CE_WARN,
1023 1026 "!apic: cpuid %d doesn't exist in apic_cpus array.",
1024 1027 cpuid);
1025 1028 return (ENODEV);
1026 1029 }
1027 1030 ASSERT((apic_cpus[cpuid].aci_status & APIC_CPU_FREE) == 0);
1028 1031
1029 1032 if (ACPI_FAILURE(acpica_unmap_cpu(cpuid))) {
1030 1033 lock_clear(&apic_ioapic_lock);
1031 1034 intr_restore(iflag);
1032 1035 return (ENOENT);
1033 1036 }
1034 1037
1035 1038 if (cpuid == apic_nproc - 1) {
1036 1039 /*
1037 1040 * We are removing the highest numbered cpuid so we need to
1038 1041 * find the next highest cpuid as the new value for apic_nproc.
1039 1042 */
1040 1043 for (i = apic_nproc; i > 0; i--) {
1041 1044 if (CPU_IN_SET(apic_cpumask, i - 1)) {
1042 1045 apic_nproc = i;
1043 1046 break;
1044 1047 }
1045 1048 }
1046 1049 /* at least one CPU left */
1047 1050 ASSERT(i > 0);
1048 1051 }
1049 1052 CPUSET_ATOMIC_DEL(apic_cpumask, cpuid);
1050 1053 /* mark slot as free and keep it in the dirty cache */
1051 1054 apic_cpus[cpuid].aci_status = APIC_CPU_FREE | APIC_CPU_DIRTY;
1052 1055
1053 1056 lock_clear(&apic_ioapic_lock);
1054 1057 intr_restore(iflag);
1055 1058
1056 1059 return (0);
1057 1060 }
1058 1061
1059 1062 /*
1060 1063 * Return the number of APIC clock ticks elapsed for 8245 to decrement
1061 1064 * (APIC_TIME_COUNT + pit_ticks_adj) ticks.
1062 1065 */
1063 1066 uint_t
1064 1067 apic_calibrate(volatile uint32_t *addr, uint16_t *pit_ticks_adj)
1065 1068 {
1066 1069 uint8_t pit_tick_lo;
1067 1070 uint16_t pit_tick, target_pit_tick;
1068 1071 uint32_t start_apic_tick, end_apic_tick;
1069 1072 ulong_t iflag;
1070 1073 uint32_t reg;
1071 1074
1072 1075 reg = addr + APIC_CURR_COUNT - apicadr;
1073 1076
1074 1077 iflag = intr_clear();
1075 1078
1076 1079 do {
1077 1080 pit_tick_lo = inb(PITCTR0_PORT);
1078 1081 pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo;
1079 1082 } while (pit_tick < APIC_TIME_MIN ||
1080 1083 pit_tick_lo <= APIC_LB_MIN || pit_tick_lo >= APIC_LB_MAX);
1081 1084
1082 1085 /*
1083 1086 * Wait for the 8254 to decrement by 5 ticks to ensure
1084 1087 * we didn't start in the middle of a tick.
1085 1088 * Compare with 0x10 for the wrap around case.
1086 1089 */
1087 1090 target_pit_tick = pit_tick - 5;
1088 1091 do {
1089 1092 pit_tick_lo = inb(PITCTR0_PORT);
1090 1093 pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo;
1091 1094 } while (pit_tick > target_pit_tick || pit_tick_lo < 0x10);
1092 1095
1093 1096 start_apic_tick = apic_reg_ops->apic_read(reg);
1094 1097
1095 1098 /*
1096 1099 * Wait for the 8254 to decrement by
1097 1100 * (APIC_TIME_COUNT + pit_ticks_adj) ticks
1098 1101 */
1099 1102 target_pit_tick = pit_tick - APIC_TIME_COUNT;
1100 1103 do {
1101 1104 pit_tick_lo = inb(PITCTR0_PORT);
1102 1105 pit_tick = (inb(PITCTR0_PORT) << 8) | pit_tick_lo;
1103 1106 } while (pit_tick > target_pit_tick || pit_tick_lo < 0x10);
1104 1107
1105 1108 end_apic_tick = apic_reg_ops->apic_read(reg);
1106 1109
1107 1110 *pit_ticks_adj = target_pit_tick - pit_tick;
1108 1111
1109 1112 intr_restore(iflag);
1110 1113
1111 1114 return (start_apic_tick - end_apic_tick);
1112 1115 }
1113 1116
1114 1117 /*
1115 1118 * Initialise the APIC timer on the local APIC of CPU 0 to the desired
1116 1119 * frequency. Note at this stage in the boot sequence, the boot processor
1117 1120 * is the only active processor.
1118 1121 * hertz value of 0 indicates a one-shot mode request. In this case
1119 1122 * the function returns the resolution (in nanoseconds) for the hardware
1120 1123 * timer interrupt. If one-shot mode capability is not available,
1121 1124 * the return value will be 0. apic_enable_oneshot is a global switch
1122 1125 * for disabling the functionality.
1123 1126 * A non-zero positive value for hertz indicates a periodic mode request.
1124 1127 * In this case the hardware will be programmed to generate clock interrupts
1125 1128 * at hertz frequency and returns the resolution of interrupts in
1126 1129 * nanosecond.
1127 1130 */
1128 1131
1129 1132 int
1130 1133 apic_clkinit(int hertz)
1131 1134 {
1132 1135 int ret;
1133 1136
1134 1137 apic_int_busy_mark = (apic_int_busy_mark *
1135 1138 apic_sample_factor_redistribution) / 100;
1136 1139 apic_int_free_mark = (apic_int_free_mark *
1137 1140 apic_sample_factor_redistribution) / 100;
1138 1141 apic_diff_for_redistribution = (apic_diff_for_redistribution *
1139 1142 apic_sample_factor_redistribution) / 100;
1140 1143
1141 1144 ret = apic_timer_init(hertz);
1142 1145 return (ret);
1143 1146
1144 1147 }
1145 1148
1146 1149 /*
1147 1150 * apic_preshutdown:
1148 1151 * Called early in shutdown whilst we can still access filesystems to do
1149 1152 * things like loading modules which will be required to complete shutdown
1150 1153 * after filesystems are all unmounted.
1151 1154 */
1152 1155 void
1153 1156 apic_preshutdown(int cmd, int fcn)
1154 1157 {
1155 1158 APIC_VERBOSE_POWEROFF(("apic_preshutdown(%d,%d); m=%d a=%d\n",
1156 1159 cmd, fcn, apic_poweroff_method, apic_enable_acpi));
1157 1160 }
1158 1161
1159 1162 void
1160 1163 apic_shutdown(int cmd, int fcn)
1161 1164 {
1162 1165 int restarts, attempts;
1163 1166 int i;
1164 1167 uchar_t byte;
1165 1168 ulong_t iflag;
1166 1169
1167 1170 hpet_acpi_fini();
1168 1171
1169 1172 /* Send NMI to all CPUs except self to do per processor shutdown */
1170 1173 iflag = intr_clear();
1171 1174 #ifdef DEBUG
1172 1175 APIC_AV_PENDING_SET();
1173 1176 #else
1174 1177 if (apic_mode == LOCAL_APIC)
1175 1178 APIC_AV_PENDING_SET();
1176 1179 #endif /* DEBUG */
1177 1180 apic_shutdown_processors = 1;
1178 1181 apic_reg_ops->apic_write(APIC_INT_CMD1,
1179 1182 AV_NMI | AV_LEVEL | AV_SH_ALL_EXCSELF);
1180 1183
1181 1184 /* restore cmos shutdown byte before reboot */
1182 1185 if (apic_cmos_ssb_set) {
1183 1186 outb(CMOS_ADDR, SSB);
1184 1187 outb(CMOS_DATA, 0);
1185 1188 }
1186 1189
1187 1190 ioapic_disable_redirection();
1188 1191
1189 1192 /* disable apic mode if imcr present */
1190 1193 if (apic_imcrp) {
1191 1194 outb(APIC_IMCR_P1, (uchar_t)APIC_IMCR_SELECT);
1192 1195 outb(APIC_IMCR_P2, (uchar_t)APIC_IMCR_PIC);
1193 1196 }
1194 1197
1195 1198 apic_disable_local_apic();
1196 1199
1197 1200 intr_restore(iflag);
1198 1201
1199 1202 /* remainder of function is for shutdown cases only */
1200 1203 if (cmd != A_SHUTDOWN)
1201 1204 return;
1202 1205
1203 1206 /*
1204 1207 * Switch system back into Legacy-Mode if using ACPI and
1205 1208 * not powering-off. Some BIOSes need to remain in ACPI-mode
1206 1209 * for power-off to succeed (Dell Dimension 4600)
1207 1210 * Do not disable ACPI while doing fastreboot
1208 1211 */
1209 1212 if (apic_enable_acpi && fcn != AD_POWEROFF && fcn != AD_FASTREBOOT)
1210 1213 (void) AcpiDisable();
1211 1214
1212 1215 if (fcn == AD_FASTREBOOT) {
1213 1216 apic_reg_ops->apic_write(APIC_INT_CMD1,
1214 1217 AV_ASSERT | AV_RESET | AV_SH_ALL_EXCSELF);
1215 1218 }
1216 1219
1217 1220 /* remainder of function is for shutdown+poweroff case only */
1218 1221 if (fcn != AD_POWEROFF)
1219 1222 return;
1220 1223
1221 1224 switch (apic_poweroff_method) {
1222 1225 case APIC_POWEROFF_VIA_RTC:
1223 1226
1224 1227 /* select the extended NVRAM bank in the RTC */
1225 1228 outb(CMOS_ADDR, RTC_REGA);
1226 1229 byte = inb(CMOS_DATA);
1227 1230 outb(CMOS_DATA, (byte | EXT_BANK));
1228 1231
1229 1232 outb(CMOS_ADDR, PFR_REG);
1230 1233
1231 1234 /* for Predator must toggle the PAB bit */
1232 1235 byte = inb(CMOS_DATA);
1233 1236
1234 1237 /*
1235 1238 * clear power active bar, wakeup alarm and
1236 1239 * kickstart
1237 1240 */
1238 1241 byte &= ~(PAB_CBIT | WF_FLAG | KS_FLAG);
1239 1242 outb(CMOS_DATA, byte);
1240 1243
1241 1244 /* delay before next write */
1242 1245 drv_usecwait(1000);
1243 1246
1244 1247 /* for S40 the following would suffice */
1245 1248 byte = inb(CMOS_DATA);
1246 1249
1247 1250 /* power active bar control bit */
1248 1251 byte |= PAB_CBIT;
1249 1252 outb(CMOS_DATA, byte);
1250 1253
1251 1254 break;
1252 1255
1253 1256 case APIC_POWEROFF_VIA_ASPEN_BMC:
1254 1257 restarts = 0;
1255 1258 restart_aspen_bmc:
1256 1259 if (++restarts == 3)
1257 1260 break;
1258 1261 attempts = 0;
1259 1262 do {
1260 1263 byte = inb(MISMIC_FLAG_REGISTER);
1261 1264 byte &= MISMIC_BUSY_MASK;
1262 1265 if (byte != 0) {
1263 1266 drv_usecwait(1000);
1264 1267 if (attempts >= 3)
1265 1268 goto restart_aspen_bmc;
1266 1269 ++attempts;
1267 1270 }
1268 1271 } while (byte != 0);
1269 1272 outb(MISMIC_CNTL_REGISTER, CC_SMS_GET_STATUS);
1270 1273 byte = inb(MISMIC_FLAG_REGISTER);
1271 1274 byte |= 0x1;
1272 1275 outb(MISMIC_FLAG_REGISTER, byte);
1273 1276 i = 0;
1274 1277 for (; i < (sizeof (aspen_bmc)/sizeof (aspen_bmc[0]));
1275 1278 i++) {
1276 1279 attempts = 0;
1277 1280 do {
1278 1281 byte = inb(MISMIC_FLAG_REGISTER);
1279 1282 byte &= MISMIC_BUSY_MASK;
1280 1283 if (byte != 0) {
1281 1284 drv_usecwait(1000);
1282 1285 if (attempts >= 3)
1283 1286 goto restart_aspen_bmc;
1284 1287 ++attempts;
1285 1288 }
1286 1289 } while (byte != 0);
1287 1290 outb(MISMIC_CNTL_REGISTER, aspen_bmc[i].cntl);
1288 1291 outb(MISMIC_DATA_REGISTER, aspen_bmc[i].data);
1289 1292 byte = inb(MISMIC_FLAG_REGISTER);
1290 1293 byte |= 0x1;
1291 1294 outb(MISMIC_FLAG_REGISTER, byte);
1292 1295 }
1293 1296 break;
1294 1297
1295 1298 case APIC_POWEROFF_VIA_SITKA_BMC:
1296 1299 restarts = 0;
1297 1300 restart_sitka_bmc:
1298 1301 if (++restarts == 3)
1299 1302 break;
1300 1303 attempts = 0;
1301 1304 do {
1302 1305 byte = inb(SMS_STATUS_REGISTER);
1303 1306 byte &= SMS_STATE_MASK;
1304 1307 if ((byte == SMS_READ_STATE) ||
1305 1308 (byte == SMS_WRITE_STATE)) {
1306 1309 drv_usecwait(1000);
1307 1310 if (attempts >= 3)
1308 1311 goto restart_sitka_bmc;
1309 1312 ++attempts;
1310 1313 }
1311 1314 } while ((byte == SMS_READ_STATE) ||
1312 1315 (byte == SMS_WRITE_STATE));
1313 1316 outb(SMS_COMMAND_REGISTER, SMS_GET_STATUS);
1314 1317 i = 0;
1315 1318 for (; i < (sizeof (sitka_bmc)/sizeof (sitka_bmc[0]));
1316 1319 i++) {
1317 1320 attempts = 0;
1318 1321 do {
1319 1322 byte = inb(SMS_STATUS_REGISTER);
1320 1323 byte &= SMS_IBF_MASK;
1321 1324 if (byte != 0) {
1322 1325 drv_usecwait(1000);
1323 1326 if (attempts >= 3)
1324 1327 goto restart_sitka_bmc;
1325 1328 ++attempts;
1326 1329 }
1327 1330 } while (byte != 0);
1328 1331 outb(sitka_bmc[i].port, sitka_bmc[i].data);
1329 1332 }
1330 1333 break;
1331 1334
1332 1335 case APIC_POWEROFF_NONE:
1333 1336
1334 1337 /* If no APIC direct method, we will try using ACPI */
1335 1338 if (apic_enable_acpi) {
1336 1339 if (acpi_poweroff() == 1)
1337 1340 return;
1338 1341 } else
1339 1342 return;
1340 1343
1341 1344 break;
1342 1345 }
1343 1346 /*
↓ open down ↓ |
1309 lines elided |
↑ open up ↑ |
1344 1347 * Wait a limited time here for power to go off.
1345 1348 * If the power does not go off, then there was a
1346 1349 * problem and we should continue to the halt which
1347 1350 * prints a message for the user to press a key to
1348 1351 * reboot.
1349 1352 */
1350 1353 drv_usecwait(7000000); /* wait seven seconds */
1351 1354
1352 1355 }
1353 1356
1354 -ddi_periodic_t apic_periodic_id;
1357 +cyclic_id_t apic_cyclic_id;
1355 1358
1356 1359 /*
1357 1360 * The following functions are in the platform specific file so that they
1358 1361 * can be different functions depending on whether we are running on
1359 1362 * bare metal or a hypervisor.
1360 1363 */
1361 1364
1362 1365 /*
1363 1366 * map an apic for memory-mapped access
1364 1367 */
1365 1368 uint32_t *
1366 1369 mapin_apic(uint32_t addr, size_t len, int flags)
1367 1370 {
1368 1371 return ((void *)psm_map_phys(addr, len, flags));
1369 1372 }
1370 1373
1371 1374 uint32_t *
1372 1375 mapin_ioapic(uint32_t addr, size_t len, int flags)
1373 1376 {
1374 1377 return (mapin_apic(addr, len, flags));
1375 1378 }
1376 1379
1377 1380 /*
1378 1381 * unmap an apic
1379 1382 */
1380 1383 void
1381 1384 mapout_apic(caddr_t addr, size_t len)
1382 1385 {
1383 1386 psm_unmap_phys(addr, len);
1384 1387 }
1385 1388
1386 1389 void
1387 1390 mapout_ioapic(caddr_t addr, size_t len)
1388 1391 {
1389 1392 mapout_apic(addr, len);
1390 1393 }
1391 1394
1392 1395 uint32_t
1393 1396 ioapic_read(int ioapic_ix, uint32_t reg)
1394 1397 {
1395 1398 volatile uint32_t *ioapic;
1396 1399
1397 1400 ioapic = apicioadr[ioapic_ix];
1398 1401 ioapic[APIC_IO_REG] = reg;
1399 1402 return (ioapic[APIC_IO_DATA]);
1400 1403 }
1401 1404
1402 1405 void
1403 1406 ioapic_write(int ioapic_ix, uint32_t reg, uint32_t value)
1404 1407 {
1405 1408 volatile uint32_t *ioapic;
1406 1409
1407 1410 ioapic = apicioadr[ioapic_ix];
1408 1411 ioapic[APIC_IO_REG] = reg;
1409 1412 ioapic[APIC_IO_DATA] = value;
1410 1413 }
1411 1414
1412 1415 void
1413 1416 ioapic_write_eoi(int ioapic_ix, uint32_t value)
1414 1417 {
1415 1418 volatile uint32_t *ioapic;
1416 1419
1417 1420 ioapic = apicioadr[ioapic_ix];
1418 1421 ioapic[APIC_IO_EOI] = value;
1419 1422 }
1420 1423
1421 1424 /*
1422 1425 * Round-robin algorithm to find the next CPU with interrupts enabled.
1423 1426 * It can't share the same static variable apic_next_bind_cpu with
1424 1427 * apic_get_next_bind_cpu(), since that will cause all interrupts to be
1425 1428 * bound to CPU1 at boot time. During boot, only CPU0 is online with
1426 1429 * interrupts enabled when apic_get_next_bind_cpu() and apic_find_cpu()
1427 1430 * are called. However, the pcplusmp driver assumes that there will be
1428 1431 * boot_ncpus CPUs configured eventually so it tries to distribute all
1429 1432 * interrupts among CPU0 - CPU[boot_ncpus - 1]. Thus to prevent all
1430 1433 * interrupts being targetted at CPU1, we need to use a dedicated static
1431 1434 * variable for find_next_cpu() instead of sharing apic_next_bind_cpu.
1432 1435 */
1433 1436
1434 1437 processorid_t
1435 1438 apic_find_cpu(int flag)
1436 1439 {
1437 1440 int i;
1438 1441 static processorid_t acid = 0;
1439 1442
1440 1443 /* Find the first CPU with the passed-in flag set */
1441 1444 for (i = 0; i < apic_nproc; i++) {
1442 1445 if (++acid >= apic_nproc) {
1443 1446 acid = 0;
1444 1447 }
1445 1448 if (apic_cpu_in_range(acid) &&
1446 1449 (apic_cpus[acid].aci_status & flag)) {
1447 1450 break;
1448 1451 }
1449 1452 }
1450 1453
1451 1454 ASSERT((apic_cpus[acid].aci_status & flag) != 0);
1452 1455 return (acid);
1453 1456 }
1454 1457
1455 1458 /*
1456 1459 * Switch between safe and x2APIC IPI sending method.
1457 1460 * CPU may power on in xapic mode or x2apic mode. If CPU needs to send IPI to
1458 1461 * other CPUs before entering x2APIC mode, it still needs to xAPIC method.
1459 1462 * Before sending StartIPI to target CPU, psm_send_ipi will be changed to
1460 1463 * apic_common_send_ipi, which detects current local APIC mode and use right
1461 1464 * method to send IPI. If some CPUs fail to start up, apic_poweron_cnt
1462 1465 * won't return to zero, so apic_common_send_ipi will always be used.
1463 1466 * psm_send_ipi can't be simply changed back to x2apic_send_ipi if some CPUs
1464 1467 * failed to start up because those failed CPUs may recover itself later at
1465 1468 * unpredictable time.
1466 1469 */
1467 1470 void
1468 1471 apic_switch_ipi_callback(boolean_t enter)
1469 1472 {
1470 1473 ulong_t iflag;
1471 1474 struct psm_ops *pops = psmops;
1472 1475
1473 1476 iflag = intr_clear();
1474 1477 lock_set(&apic_mode_switch_lock);
1475 1478 if (enter) {
1476 1479 ASSERT(apic_poweron_cnt >= 0);
1477 1480 if (apic_poweron_cnt == 0) {
1478 1481 pops->psm_send_ipi = apic_common_send_ipi;
1479 1482 send_dirintf = pops->psm_send_ipi;
1480 1483 }
1481 1484 apic_poweron_cnt++;
1482 1485 } else {
1483 1486 ASSERT(apic_poweron_cnt > 0);
1484 1487 apic_poweron_cnt--;
1485 1488 if (apic_poweron_cnt == 0) {
1486 1489 pops->psm_send_ipi = x2apic_send_ipi;
1487 1490 send_dirintf = pops->psm_send_ipi;
1488 1491 }
1489 1492 }
1490 1493 lock_clear(&apic_mode_switch_lock);
1491 1494 intr_restore(iflag);
1492 1495 }
1493 1496
1494 1497 void
1495 1498 apic_intrmap_init(int apic_mode)
1496 1499 {
1497 1500 int suppress_brdcst_eoi = 0;
1498 1501
1499 1502 if (psm_vt_ops != NULL) {
1500 1503 /*
1501 1504 * Since X2APIC requires the use of interrupt remapping
1502 1505 * (though this is not documented explicitly in the Intel
1503 1506 * documentation (yet)), initialize interrupt remapping
1504 1507 * support before initializing the X2APIC unit.
1505 1508 */
1506 1509 if (((apic_intrmap_ops_t *)psm_vt_ops)->
1507 1510 apic_intrmap_init(apic_mode) == DDI_SUCCESS) {
1508 1511
1509 1512 apic_vt_ops = psm_vt_ops;
1510 1513
1511 1514 /*
1512 1515 * We leverage the interrupt remapping engine to
1513 1516 * suppress broadcast EOI; thus we must send the
1514 1517 * directed EOI with the directed-EOI handler.
1515 1518 */
1516 1519 if (apic_directed_EOI_supported() == 0) {
1517 1520 suppress_brdcst_eoi = 1;
1518 1521 }
1519 1522
1520 1523 apic_vt_ops->apic_intrmap_enable(suppress_brdcst_eoi);
1521 1524
1522 1525 if (apic_detect_x2apic()) {
1523 1526 apic_enable_x2apic();
1524 1527 }
1525 1528
1526 1529 if (apic_directed_EOI_supported() == 0) {
1527 1530 apic_set_directed_EOI_handler();
1528 1531 }
1529 1532 }
1530 1533 }
1531 1534 }
1532 1535
1533 1536 /*ARGSUSED*/
1534 1537 static void
1535 1538 apic_record_ioapic_rdt(void *intrmap_private, ioapic_rdt_t *irdt)
1536 1539 {
1537 1540 irdt->ir_hi <<= APIC_ID_BIT_OFFSET;
1538 1541 }
1539 1542
1540 1543 /*ARGSUSED*/
1541 1544 static void
1542 1545 apic_record_msi(void *intrmap_private, msi_regs_t *mregs)
1543 1546 {
1544 1547 mregs->mr_addr = MSI_ADDR_HDR |
1545 1548 (MSI_ADDR_RH_FIXED << MSI_ADDR_RH_SHIFT) |
1546 1549 (MSI_ADDR_DM_PHYSICAL << MSI_ADDR_DM_SHIFT) |
1547 1550 (mregs->mr_addr << MSI_ADDR_DEST_SHIFT);
1548 1551 mregs->mr_data = (MSI_DATA_TM_EDGE << MSI_DATA_TM_SHIFT) |
1549 1552 mregs->mr_data;
1550 1553 }
1551 1554
1552 1555 /*
1553 1556 * Functions from apic_introp.c
1554 1557 *
1555 1558 * Those functions are used by apic_intr_ops().
1556 1559 */
1557 1560
1558 1561 /*
1559 1562 * MSI support flag:
1560 1563 * reflects whether MSI is supported at APIC level
1561 1564 * it can also be patched through /etc/system
1562 1565 *
1563 1566 * 0 = default value - don't know and need to call apic_check_msi_support()
1564 1567 * to find out then set it accordingly
1565 1568 * 1 = supported
1566 1569 * -1 = not supported
1567 1570 */
1568 1571 int apic_support_msi = 0;
1569 1572
1570 1573 /* Multiple vector support for MSI-X */
1571 1574 int apic_msix_enable = 1;
1572 1575
1573 1576 /* Multiple vector support for MSI */
1574 1577 int apic_multi_msi_enable = 1;
1575 1578
1576 1579 /*
1577 1580 * check whether the system supports MSI
1578 1581 *
1579 1582 * If PCI-E capability is found, then this must be a PCI-E system.
1580 1583 * Since MSI is required for PCI-E system, it returns PSM_SUCCESS
1581 1584 * to indicate this system supports MSI.
1582 1585 */
1583 1586 int
1584 1587 apic_check_msi_support()
1585 1588 {
1586 1589 dev_info_t *cdip;
1587 1590 char dev_type[16];
1588 1591 int dev_len;
1589 1592
1590 1593 DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support:\n"));
1591 1594
1592 1595 /*
1593 1596 * check whether the first level children of root_node have
1594 1597 * PCI-E capability
1595 1598 */
1596 1599 for (cdip = ddi_get_child(ddi_root_node()); cdip != NULL;
1597 1600 cdip = ddi_get_next_sibling(cdip)) {
1598 1601
1599 1602 DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: cdip: 0x%p,"
1600 1603 " driver: %s, binding: %s, nodename: %s\n", (void *)cdip,
1601 1604 ddi_driver_name(cdip), ddi_binding_name(cdip),
1602 1605 ddi_node_name(cdip)));
1603 1606 dev_len = sizeof (dev_type);
1604 1607 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, DDI_PROP_DONTPASS,
1605 1608 "device_type", (caddr_t)dev_type, &dev_len)
1606 1609 != DDI_PROP_SUCCESS)
1607 1610 continue;
1608 1611 if (strcmp(dev_type, "pciex") == 0)
1609 1612 return (PSM_SUCCESS);
1610 1613 }
1611 1614
1612 1615 /* MSI is not supported on this system */
1613 1616 DDI_INTR_IMPLDBG((CE_CONT, "apic_check_msi_support: no 'pciex' "
1614 1617 "device_type found\n"));
1615 1618 return (PSM_FAILURE);
1616 1619 }
1617 1620
1618 1621 /*
1619 1622 * apic_pci_msi_unconfigure:
1620 1623 *
1621 1624 * This and next two interfaces are copied from pci_intr_lib.c
1622 1625 * Do ensure that these two files stay in sync.
1623 1626 * These needed to be copied over here to avoid a deadlock situation on
1624 1627 * certain mp systems that use MSI interrupts.
1625 1628 *
1626 1629 * IMPORTANT regards next three interfaces:
1627 1630 * i) are called only for MSI/X interrupts.
1628 1631 * ii) called with interrupts disabled, and must not block
1629 1632 */
1630 1633 void
1631 1634 apic_pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
1632 1635 {
1633 1636 ushort_t msi_ctrl;
1634 1637 int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip);
1635 1638 ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip);
1636 1639
1637 1640 ASSERT((handle != NULL) && (cap_ptr != 0));
1638 1641
1639 1642 if (type == DDI_INTR_TYPE_MSI) {
1640 1643 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
1641 1644 msi_ctrl &= (~PCI_MSI_MME_MASK);
1642 1645 pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
1643 1646 pci_config_put32(handle, cap_ptr + PCI_MSI_ADDR_OFFSET, 0);
1644 1647
1645 1648 if (msi_ctrl & PCI_MSI_64BIT_MASK) {
1646 1649 pci_config_put16(handle,
1647 1650 cap_ptr + PCI_MSI_64BIT_DATA, 0);
1648 1651 pci_config_put32(handle,
1649 1652 cap_ptr + PCI_MSI_ADDR_OFFSET + 4, 0);
1650 1653 } else {
1651 1654 pci_config_put16(handle,
1652 1655 cap_ptr + PCI_MSI_32BIT_DATA, 0);
1653 1656 }
1654 1657
1655 1658 } else if (type == DDI_INTR_TYPE_MSIX) {
1656 1659 uintptr_t off;
1657 1660 uint32_t mask;
1658 1661 ddi_intr_msix_t *msix_p = i_ddi_get_msix(rdip);
1659 1662
1660 1663 ASSERT(msix_p != NULL);
1661 1664
1662 1665 /* Offset into "inum"th entry in the MSI-X table & mask it */
1663 1666 off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
1664 1667 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
1665 1668
1666 1669 mask = ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off);
1667 1670
1668 1671 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, (mask | 1));
1669 1672
1670 1673 /* Offset into the "inum"th entry in the MSI-X table */
1671 1674 off = (uintptr_t)msix_p->msix_tbl_addr +
1672 1675 (inum * PCI_MSIX_VECTOR_SIZE);
1673 1676
1674 1677 /* Reset the "data" and "addr" bits */
1675 1678 ddi_put32(msix_p->msix_tbl_hdl,
1676 1679 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);
1677 1680 ddi_put64(msix_p->msix_tbl_hdl, (uint64_t *)off, 0);
1678 1681 }
1679 1682 }
1680 1683
1681 1684 /*
1682 1685 * apic_pci_msi_disable_mode:
1683 1686 */
1684 1687 void
1685 1688 apic_pci_msi_disable_mode(dev_info_t *rdip, int type)
1686 1689 {
1687 1690 ushort_t msi_ctrl;
1688 1691 int cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip);
1689 1692 ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(rdip);
1690 1693
1691 1694 ASSERT((handle != NULL) && (cap_ptr != 0));
1692 1695
1693 1696 if (type == DDI_INTR_TYPE_MSI) {
1694 1697 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL);
1695 1698 if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
1696 1699 return;
1697 1700
1698 1701 msi_ctrl &= ~PCI_MSI_ENABLE_BIT; /* MSI disable */
1699 1702 pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl);
1700 1703
1701 1704 } else if (type == DDI_INTR_TYPE_MSIX) {
1702 1705 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL);
1703 1706 if (msi_ctrl & PCI_MSIX_ENABLE_BIT) {
1704 1707 msi_ctrl &= ~PCI_MSIX_ENABLE_BIT;
1705 1708 pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL,
1706 1709 msi_ctrl);
1707 1710 }
1708 1711 }
1709 1712 }
1710 1713
1711 1714 uint32_t
1712 1715 apic_get_localapicid(uint32_t cpuid)
1713 1716 {
1714 1717 ASSERT(cpuid < apic_nproc && apic_cpus != NULL);
1715 1718
1716 1719 return (apic_cpus[cpuid].aci_local_id);
1717 1720 }
1718 1721
1719 1722 uchar_t
1720 1723 apic_get_ioapicid(uchar_t ioapicindex)
1721 1724 {
1722 1725 ASSERT(ioapicindex < MAX_IO_APIC);
1723 1726
1724 1727 return (apic_io_id[ioapicindex]);
1725 1728 }
↓ open down ↓ |
361 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX