1 /* 2 * Copyright (c) 2009 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Sepherosa Ziehau <sepherosa@gmail.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/bus.h> 37 #include <sys/kernel.h> 38 #include <sys/systm.h> 39 40 #include <machine_base/isa/isa_intr.h> 41 #include <machine_base/apic/lapic.h> 42 #include <machine_base/apic/ioapic.h> 43 #include <machine_base/apic/apicvar.h> 44 45 #include <contrib/dev/acpica/source/include/acpi.h> 46 47 #include "acpi_sdt_var.h" 48 #include "acpi_sci_var.h" 49 50 extern int naps; 51 52 #define MADT_VPRINTF(fmt, arg...) \ 53 do { \ 54 if (bootverbose) \ 55 kprintf("ACPI MADT: " fmt , ##arg); \ 56 } while (0) 57 58 #define MADT_INT_BUS_ISA 0 59 60 #define MADT_INT_POLA_SHIFT 0 61 #define MADT_INT_TRIG_SHIFT 2 62 63 typedef int (*madt_iter_t)(void *, 64 const ACPI_SUBTABLE_HEADER *); 65 66 static int madt_check(vm_paddr_t); 67 static int madt_iterate_entries(ACPI_TABLE_MADT *, 68 madt_iter_t, void *); 69 70 static vm_paddr_t madt_lapic_pass1(void); 71 static int madt_lapic_pass2(int); 72 73 static int madt_lapic_enumerate(struct lapic_enumerator *); 74 static int madt_lapic_probe(struct lapic_enumerator *); 75 76 static void madt_ioapic_enumerate( 77 struct ioapic_enumerator *); 78 static int madt_ioapic_probe(struct ioapic_enumerator *); 79 80 static vm_paddr_t madt_phyaddr; 81 82 static void 83 madt_probe(void) 84 { 85 vm_paddr_t madt_paddr; 86 87 KKASSERT(madt_phyaddr == 0); 88 89 madt_paddr = sdt_search(ACPI_SIG_MADT); 90 if (madt_paddr == 0) { 91 kprintf("madt_probe: can't locate MADT\n"); 92 return; 93 } 94 95 /* Preliminary checks */ 96 if (madt_check(madt_paddr)) { 97 kprintf("madt_probe: madt_check failed\n"); 98 return; 99 } 100 101 madt_phyaddr = madt_paddr; 102 } 103 SYSINIT(madt_probe, SI_BOOT2_PRESMP, SI_ORDER_SECOND, madt_probe, 0); 104 105 static int 106 madt_check(vm_paddr_t madt_paddr) 107 { 108 ACPI_TABLE_MADT *madt; 109 int error = 0; 110 111 KKASSERT(madt_paddr != 0); 112 113 madt = sdt_sdth_map(madt_paddr); 114 KKASSERT(madt != NULL); 115 116 /* 117 * MADT in ACPI specification 1.0 - 5.0 118 */ 119 if (madt->Header.Revision < 1 || madt->Header.Revision > 3) { 120 kprintf("madt_check: unknown MADT revision %d\n", 121 madt->Header.Revision); 122 } 123 124 if (madt->Header.Length < sizeof(*madt)) { 125 kprintf("madt_check: invalid MADT length %u\n", 126 madt->Header.Length); 127 error = EINVAL; 128 goto back; 129 } 130 back: 131 sdt_sdth_unmap(&madt->Header); 132 return error; 133 } 134 135 static int 136 madt_iterate_entries(ACPI_TABLE_MADT *madt, madt_iter_t func, void *arg) 137 { 138 int size, cur, error; 139 140 size = madt->Header.Length - sizeof(*madt); 141 cur = 0; 142 error = 0; 143 144 while (size - cur > sizeof(ACPI_SUBTABLE_HEADER)) { 145 const ACPI_SUBTABLE_HEADER *ent; 146 147 ent = (const ACPI_SUBTABLE_HEADER *)((char *)madt + 148 sizeof(*madt) + cur); 149 if (ent->Length < sizeof(*ent)) { 150 kprintf("madt_iterate_entries: invalid MADT " 151 "entry len %d\n", ent->Length); 152 error = EINVAL; 153 break; 154 } 155 if (ent->Length > (size - cur)) { 156 kprintf("madt_iterate_entries: invalid MADT " 157 "entry len %d, > table length\n", ent->Length); 158 error = EINVAL; 159 break; 160 } 161 162 cur += ent->Length; 163 164 /* 165 * Only Local APIC, I/O APIC and Interrupt Source Override 166 * are defined in ACPI specification 1.0 - 5.0 167 */ 168 switch (ent->Type) { 169 case ACPI_MADT_TYPE_LOCAL_APIC: 170 if (ent->Length < sizeof(ACPI_MADT_LOCAL_APIC)) { 171 kprintf("madt_iterate_entries: invalid MADT " 172 "lapic entry len %d\n", ent->Length); 173 error = EINVAL; 174 } 175 break; 176 177 case ACPI_MADT_TYPE_IO_APIC: 178 if (ent->Length < sizeof(ACPI_MADT_IO_APIC)) { 179 kprintf("madt_iterate_entries: invalid MADT " 180 "ioapic entry len %d\n", ent->Length); 181 error = EINVAL; 182 } 183 break; 184 185 case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE: 186 if (ent->Length < sizeof(ACPI_MADT_INTERRUPT_OVERRIDE)) { 187 kprintf("madt_iterate_entries: invalid MADT " 188 "intsrc entry len %d\n", 189 ent->Length); 190 error = EINVAL; 191 } 192 break; 193 } 194 if (error) 195 break; 196 197 error = func(arg, ent); 198 if (error) 199 break; 200 201 ent = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, ent, ent->Length); 202 } 203 return error; 204 } 205 206 static int 207 madt_lapic_pass1_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 208 { 209 const ACPI_MADT_LOCAL_APIC_OVERRIDE *lapic_addr_ent; 210 uint64_t *addr64 = xarg; 211 212 if (ent->Type != ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE) 213 return 0; 214 if (ent->Length < sizeof(*lapic_addr_ent)) { 215 kprintf("madt_lapic_pass1: " 216 "invalid LAPIC address override length\n"); 217 return 0; 218 } 219 lapic_addr_ent = (const ACPI_MADT_LOCAL_APIC_OVERRIDE *)ent; 220 221 *addr64 = lapic_addr_ent->Address; 222 return 0; 223 } 224 225 static vm_paddr_t 226 madt_lapic_pass1(void) 227 { 228 ACPI_TABLE_MADT *madt; 229 vm_paddr_t lapic_addr; 230 uint64_t lapic_addr64; 231 int error; 232 233 KKASSERT(madt_phyaddr != 0); 234 235 madt = sdt_sdth_map(madt_phyaddr); 236 KKASSERT(madt != NULL); 237 238 MADT_VPRINTF("LAPIC address 0x%x, flags %#x\n", 239 madt->Address, madt->Flags); 240 lapic_addr = madt->Address; 241 242 lapic_addr64 = 0; 243 error = madt_iterate_entries(madt, madt_lapic_pass1_callback, 244 &lapic_addr64); 245 if (error) 246 panic("madt_iterate_entries(pass1) failed"); 247 248 if (lapic_addr64 != 0) { 249 kprintf("ACPI MADT: 64bits lapic address 0x%lx\n", 250 lapic_addr64); 251 lapic_addr = lapic_addr64; 252 } 253 254 sdt_sdth_unmap(&madt->Header); 255 256 return lapic_addr; 257 } 258 259 struct madt_lapic_pass2_cbarg { 260 int cpu; 261 int bsp_found; 262 int bsp_apic_id; 263 }; 264 265 static int 266 madt_lapic_pass2_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 267 { 268 const ACPI_MADT_LOCAL_APIC *lapic_ent; 269 struct madt_lapic_pass2_cbarg *arg = xarg; 270 271 if (ent->Type != ACPI_MADT_TYPE_LOCAL_APIC) 272 return 0; 273 274 lapic_ent = (const ACPI_MADT_LOCAL_APIC *)ent; 275 if (lapic_ent->LapicFlags & ACPI_MADT_ENABLED) { 276 MADT_VPRINTF("cpu id %d, apic id %d\n", 277 lapic_ent->ProcessorId, lapic_ent->Id); 278 if (lapic_ent->Id == arg->bsp_apic_id) { 279 lapic_set_cpuid(0, lapic_ent->Id); 280 arg->bsp_found = 1; 281 } else { 282 lapic_set_cpuid(arg->cpu, lapic_ent->Id); 283 arg->cpu++; 284 } 285 } 286 return 0; 287 } 288 289 static int 290 madt_lapic_pass2(int bsp_apic_id) 291 { 292 ACPI_TABLE_MADT *madt; 293 struct madt_lapic_pass2_cbarg arg; 294 int error; 295 296 MADT_VPRINTF("BSP apic id %d\n", bsp_apic_id); 297 298 KKASSERT(madt_phyaddr != 0); 299 300 madt = sdt_sdth_map(madt_phyaddr); 301 KKASSERT(madt != NULL); 302 303 bzero(&arg, sizeof(arg)); 304 arg.cpu = 1; 305 arg.bsp_apic_id = bsp_apic_id; 306 307 error = madt_iterate_entries(madt, madt_lapic_pass2_callback, &arg); 308 if (error) 309 panic("madt_iterate_entries(pass2) failed"); 310 311 KKASSERT(arg.bsp_found); 312 naps = arg.cpu - 1; /* exclude BSP */ 313 314 sdt_sdth_unmap(&madt->Header); 315 316 return 0; 317 } 318 319 struct madt_lapic_probe_cbarg { 320 int cpu_count; 321 vm_paddr_t lapic_addr; 322 }; 323 324 static int 325 madt_lapic_probe_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 326 { 327 struct madt_lapic_probe_cbarg *arg = xarg; 328 329 if (ent->Type == ACPI_MADT_TYPE_LOCAL_APIC) { 330 const ACPI_MADT_LOCAL_APIC *lapic_ent; 331 332 lapic_ent = (const ACPI_MADT_LOCAL_APIC *)ent; 333 if (lapic_ent->LapicFlags & ACPI_MADT_ENABLED) { 334 arg->cpu_count++; 335 if (lapic_ent->Id == APICID_MAX) { 336 kprintf("madt_lapic_probe: " 337 "invalid LAPIC apic id %d\n", 338 lapic_ent->Id); 339 return EINVAL; 340 } 341 } 342 } else if (ent->Type == ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE) { 343 const ACPI_MADT_LOCAL_APIC_OVERRIDE *lapic_addr_ent; 344 345 if (ent->Length < sizeof(*lapic_addr_ent)) { 346 kprintf("madt_lapic_probe: " 347 "invalid LAPIC address override length\n"); 348 return 0; 349 } 350 lapic_addr_ent = (const ACPI_MADT_LOCAL_APIC_OVERRIDE *)ent; 351 352 if (lapic_addr_ent->Address != 0) 353 arg->lapic_addr = lapic_addr_ent->Address; 354 } 355 return 0; 356 } 357 358 static int 359 madt_lapic_probe(struct lapic_enumerator *e) 360 { 361 struct madt_lapic_probe_cbarg arg; 362 ACPI_TABLE_MADT *madt; 363 int error; 364 365 if (madt_phyaddr == 0) 366 return ENXIO; 367 368 madt = sdt_sdth_map(madt_phyaddr); 369 KKASSERT(madt != NULL); 370 371 bzero(&arg, sizeof(arg)); 372 arg.lapic_addr = madt->Address; 373 374 error = madt_iterate_entries(madt, madt_lapic_probe_callback, &arg); 375 if (!error) { 376 if (arg.cpu_count == 0) { 377 kprintf("madt_lapic_probe: no CPU is found\n"); 378 error = EOPNOTSUPP; 379 } 380 if (arg.lapic_addr == 0) { 381 kprintf("madt_lapic_probe: zero LAPIC address\n"); 382 error = EOPNOTSUPP; 383 } 384 } 385 386 sdt_sdth_unmap(&madt->Header); 387 return error; 388 } 389 390 static int 391 madt_lapic_enumerate(struct lapic_enumerator *e) 392 { 393 vm_paddr_t lapic_addr; 394 int bsp_apic_id; 395 396 KKASSERT(madt_phyaddr != 0); 397 398 lapic_addr = madt_lapic_pass1(); 399 if (lapic_addr == 0) 400 panic("madt_lapic_enumerate: no local apic"); 401 402 lapic_map(lapic_addr); 403 404 bsp_apic_id = APIC_ID(lapic->id); 405 if (bsp_apic_id == APICID_MAX) { 406 /* 407 * XXX 408 * Some old brain dead BIOS will set BSP's LAPIC apic id 409 * to 255, though all LAPIC entries in MADT are valid. 410 */ 411 kprintf("%s invalid BSP LAPIC apic id %d\n", __func__, 412 bsp_apic_id); 413 return EINVAL; 414 } 415 416 if (madt_lapic_pass2(bsp_apic_id)) 417 panic("madt_lapic_enumerate: madt_lapic_pass2 failed"); 418 419 return 0; 420 } 421 422 static struct lapic_enumerator madt_lapic_enumerator = { 423 .lapic_prio = LAPIC_ENUM_PRIO_MADT, 424 .lapic_probe = madt_lapic_probe, 425 .lapic_enumerate = madt_lapic_enumerate 426 }; 427 428 static void 429 madt_lapic_enum_register(void) 430 { 431 int prio; 432 433 prio = LAPIC_ENUM_PRIO_MADT; 434 kgetenv_int("hw.madt_lapic_prio", &prio); 435 madt_lapic_enumerator.lapic_prio = prio; 436 437 lapic_enumerator_register(&madt_lapic_enumerator); 438 } 439 SYSINIT(madt_lapic, SI_BOOT2_PRESMP, SI_ORDER_ANY, madt_lapic_enum_register, 0); 440 441 struct madt_ioapic_probe_cbarg { 442 int ioapic_cnt; 443 int gsi_base0; 444 }; 445 446 static int 447 madt_ioapic_probe_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 448 { 449 struct madt_ioapic_probe_cbarg *arg = xarg; 450 451 if (ent->Type == ACPI_MADT_TYPE_INTERRUPT_OVERRIDE) { 452 const ACPI_MADT_INTERRUPT_OVERRIDE *intsrc_ent; 453 int trig, pola; 454 455 intsrc_ent = (const ACPI_MADT_INTERRUPT_OVERRIDE *)ent; 456 457 if (intsrc_ent->SourceIrq >= ISA_IRQ_CNT) { 458 kprintf("madt_ioapic_probe: invalid intsrc irq (%d)\n", 459 intsrc_ent->SourceIrq); 460 return EINVAL; 461 } 462 463 if (intsrc_ent->Bus != MADT_INT_BUS_ISA) { 464 kprintf("ACPI MADT: warning intsrc irq %d " 465 "bus is not ISA (%d)\n", 466 intsrc_ent->SourceIrq, intsrc_ent->Bus); 467 } 468 469 trig = (intsrc_ent->IntiFlags & ACPI_MADT_TRIGGER_MASK) >> 470 MADT_INT_TRIG_SHIFT; 471 if (trig == ACPI_MADT_TRIGGER_RESERVED) { 472 kprintf("ACPI MADT: warning invalid intsrc irq %d " 473 "trig, reserved\n", intsrc_ent->SourceIrq); 474 } else if (trig == ACPI_MADT_TRIGGER_LEVEL) { 475 MADT_VPRINTF("warning invalid intsrc irq %d " 476 "trig, level\n", intsrc_ent->SourceIrq); 477 } 478 479 pola = (intsrc_ent->IntiFlags & ACPI_MADT_POLARITY_MASK) >> 480 MADT_INT_POLA_SHIFT; 481 if (pola == ACPI_MADT_POLARITY_RESERVED) { 482 kprintf("ACPI MADT: warning invalid intsrc irq %d " 483 "pola, reserved\n", intsrc_ent->SourceIrq); 484 } else if (pola == ACPI_MADT_POLARITY_ACTIVE_LOW) { 485 MADT_VPRINTF("warning invalid intsrc irq %d " 486 "pola, low\n", intsrc_ent->SourceIrq); 487 } 488 } else if (ent->Type == ACPI_MADT_TYPE_IO_APIC) { 489 const ACPI_MADT_IO_APIC *ioapic_ent; 490 491 ioapic_ent = (const ACPI_MADT_IO_APIC *)ent; 492 if (ioapic_ent->Address == 0) { 493 kprintf("madt_ioapic_probe: zero IOAPIC address\n"); 494 return EINVAL; 495 } 496 if (ioapic_ent->Id == APICID_MAX) { 497 kprintf("madt_ioapic_probe: " 498 "invalid IOAPIC apic id %d\n", 499 ioapic_ent->Id); 500 return EINVAL; 501 } 502 503 arg->ioapic_cnt++; 504 if (ioapic_ent->GlobalIrqBase == 0) 505 arg->gsi_base0 = 1; 506 } 507 return 0; 508 } 509 510 static int 511 madt_ioapic_probe(struct ioapic_enumerator *e) 512 { 513 struct madt_ioapic_probe_cbarg arg; 514 ACPI_TABLE_MADT *madt; 515 int error; 516 517 if (madt_phyaddr == 0) 518 return ENXIO; 519 520 madt = sdt_sdth_map(madt_phyaddr); 521 KKASSERT(madt != NULL); 522 523 bzero(&arg, sizeof(arg)); 524 525 error = madt_iterate_entries(madt, madt_ioapic_probe_callback, &arg); 526 if (!error) { 527 if (arg.ioapic_cnt == 0) { 528 kprintf("madt_ioapic_probe: no IOAPIC\n"); 529 error = ENXIO; 530 } 531 if (!arg.gsi_base0) { 532 kprintf("madt_ioapic_probe: no GSI base 0\n"); 533 error = EINVAL; 534 } 535 } 536 537 sdt_sdth_unmap(&madt->Header); 538 return error; 539 } 540 541 static int 542 madt_ioapic_enum_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 543 { 544 if (ent->Type == ACPI_MADT_TYPE_INTERRUPT_OVERRIDE) { 545 const ACPI_MADT_INTERRUPT_OVERRIDE *intsrc_ent; 546 enum intr_trigger trig; 547 enum intr_polarity pola; 548 int ent_trig, ent_pola; 549 550 intsrc_ent = (const ACPI_MADT_INTERRUPT_OVERRIDE *)ent; 551 552 KKASSERT(intsrc_ent->SourceIrq < ISA_IRQ_CNT); 553 if (intsrc_ent->Bus != MADT_INT_BUS_ISA) 554 return 0; 555 556 ent_trig = (intsrc_ent->IntiFlags & ACPI_MADT_TRIGGER_MASK) >> 557 MADT_INT_TRIG_SHIFT; 558 if (ent_trig == ACPI_MADT_TRIGGER_RESERVED) 559 return 0; 560 else if (ent_trig == ACPI_MADT_TRIGGER_LEVEL) 561 trig = INTR_TRIGGER_LEVEL; 562 else 563 trig = INTR_TRIGGER_EDGE; 564 565 ent_pola = (intsrc_ent->IntiFlags & ACPI_MADT_POLARITY_MASK) >> 566 MADT_INT_POLA_SHIFT; 567 if (ent_pola == ACPI_MADT_POLARITY_RESERVED) 568 return 0; 569 else if (ent_pola == ACPI_MADT_POLARITY_ACTIVE_LOW) 570 pola = INTR_POLARITY_LOW; 571 else 572 pola = INTR_POLARITY_HIGH; 573 574 if (intsrc_ent->SourceIrq == acpi_sci_irqno()) { 575 acpi_sci_setmode1(trig, pola); 576 MADT_VPRINTF("SCI irq %d, first test %s/%s\n", 577 intsrc_ent->SourceIrq, 578 intr_str_trigger(trig), intr_str_polarity(pola)); 579 } 580 581 /* 582 * We ignore the polarity and trigger changes, since 583 * most of them are wrong or useless at best. 584 */ 585 if (intsrc_ent->SourceIrq == intsrc_ent->GlobalIrq) { 586 /* Nothing changed */ 587 return 0; 588 } 589 trig = INTR_TRIGGER_EDGE; 590 pola = INTR_POLARITY_HIGH; 591 592 MADT_VPRINTF("INTSRC irq %d -> gsi %u %s/%s\n", 593 intsrc_ent->SourceIrq, intsrc_ent->GlobalIrq, 594 intr_str_trigger(trig), intr_str_polarity(pola)); 595 ioapic_intsrc(intsrc_ent->SourceIrq, intsrc_ent->GlobalIrq, 596 trig, pola); 597 } else if (ent->Type == ACPI_MADT_TYPE_IO_APIC) { 598 const ACPI_MADT_IO_APIC *ioapic_ent; 599 uint32_t ver; 600 void *addr; 601 int npin; 602 603 ioapic_ent = (const ACPI_MADT_IO_APIC *)ent; 604 MADT_VPRINTF("IOAPIC addr 0x%08x, apic id %d, gsi base %u\n", 605 ioapic_ent->Address, ioapic_ent->Id, 606 ioapic_ent->GlobalIrqBase); 607 608 addr = ioapic_map(ioapic_ent->Address); 609 610 ver = ioapic_read(addr, IOAPIC_VER); 611 npin = ((ver & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1; 612 613 ioapic_add(addr, ioapic_ent->GlobalIrqBase, npin); 614 } 615 return 0; 616 } 617 618 static void 619 madt_ioapic_enumerate(struct ioapic_enumerator *e) 620 { 621 ACPI_TABLE_MADT *madt; 622 int error; 623 624 KKASSERT(madt_phyaddr != 0); 625 626 madt = sdt_sdth_map(madt_phyaddr); 627 KKASSERT(madt != NULL); 628 629 error = madt_iterate_entries(madt, madt_ioapic_enum_callback, NULL); 630 if (error) 631 panic("madt_ioapic_enumerate failed"); 632 633 sdt_sdth_unmap(&madt->Header); 634 } 635 636 static struct ioapic_enumerator madt_ioapic_enumerator = { 637 .ioapic_prio = IOAPIC_ENUM_PRIO_MADT, 638 .ioapic_probe = madt_ioapic_probe, 639 .ioapic_enumerate = madt_ioapic_enumerate 640 }; 641 642 static void 643 madt_ioapic_enum_register(void) 644 { 645 int prio; 646 647 prio = IOAPIC_ENUM_PRIO_MADT; 648 kgetenv_int("hw.madt_ioapic_prio", &prio); 649 madt_ioapic_enumerator.ioapic_prio = prio; 650 651 ioapic_enumerator_register(&madt_ioapic_enumerator); 652 } 653 SYSINIT(madt_ioapic, SI_BOOT2_PRESMP, SI_ORDER_ANY, 654 madt_ioapic_enum_register, 0); 655