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 typedef int (*madt_iter_t)(void *, 61 const ACPI_SUBTABLE_HEADER *); 62 63 static int madt_check(vm_paddr_t); 64 static int madt_iterate_entries(ACPI_TABLE_MADT *, 65 madt_iter_t, void *); 66 67 static vm_paddr_t madt_lapic_pass1(void); 68 static int madt_lapic_pass2(int); 69 70 static int madt_lapic_enumerate(struct lapic_enumerator *); 71 static int madt_lapic_probe(struct lapic_enumerator *); 72 73 static void madt_ioapic_enumerate( 74 struct ioapic_enumerator *); 75 static int madt_ioapic_probe(struct ioapic_enumerator *); 76 77 static vm_paddr_t madt_phyaddr; 78 79 static void 80 madt_probe(void) 81 { 82 vm_paddr_t madt_paddr; 83 84 KKASSERT(madt_phyaddr == 0); 85 86 madt_paddr = sdt_search(ACPI_SIG_MADT); 87 if (madt_paddr == 0) { 88 kprintf("madt_probe: can't locate MADT\n"); 89 return; 90 } 91 92 /* Preliminary checks */ 93 if (madt_check(madt_paddr)) { 94 kprintf("madt_probe: madt_check failed\n"); 95 return; 96 } 97 98 madt_phyaddr = madt_paddr; 99 } 100 SYSINIT(madt_probe, SI_BOOT2_PRESMP, SI_ORDER_SECOND, madt_probe, 0); 101 102 static int 103 madt_check(vm_paddr_t madt_paddr) 104 { 105 ACPI_TABLE_MADT *madt; 106 int error = 0; 107 108 KKASSERT(madt_paddr != 0); 109 110 madt = sdt_sdth_map(madt_paddr); 111 KKASSERT(madt != NULL); 112 113 /* 114 * MADT in ACPI specification 1.0 - 5.0 115 */ 116 if (madt->Header.Revision < 1 || madt->Header.Revision > 3) { 117 kprintf("madt_check: unknown MADT revision %d\n", 118 madt->Header.Revision); 119 } 120 121 if (madt->Header.Length < sizeof(*madt)) { 122 kprintf("madt_check: invalid MADT length %u\n", 123 madt->Header.Length); 124 error = EINVAL; 125 goto back; 126 } 127 back: 128 sdt_sdth_unmap(&madt->Header); 129 return error; 130 } 131 132 static int 133 madt_iterate_entries(ACPI_TABLE_MADT *madt, madt_iter_t func, void *arg) 134 { 135 int size, cur, error; 136 137 size = madt->Header.Length - sizeof(*madt); 138 cur = 0; 139 error = 0; 140 141 while (size - cur > sizeof(ACPI_SUBTABLE_HEADER)) { 142 const ACPI_SUBTABLE_HEADER *ent; 143 144 ent = (const ACPI_SUBTABLE_HEADER *) 145 ((char *)madt + sizeof(*madt) + cur); 146 if (ent->Length < sizeof(*ent)) { 147 kprintf("madt_iterate_entries: invalid MADT " 148 "entry len %d\n", ent->Length); 149 error = EINVAL; 150 break; 151 } 152 if (ent->Length > (size - cur)) { 153 kprintf("madt_iterate_entries: invalid MADT " 154 "entry len %d, > table length\n", ent->Length); 155 error = EINVAL; 156 break; 157 } 158 159 cur += ent->Length; 160 161 /* 162 * Only Local APIC, I/O APIC and Interrupt Source Override 163 * are defined in ACPI specification 1.0 - 5.0 164 */ 165 switch (ent->Type) { 166 case ACPI_MADT_TYPE_LOCAL_APIC: 167 if (ent->Length < sizeof(ACPI_MADT_LOCAL_APIC)) { 168 kprintf("madt_iterate_entries: invalid MADT " 169 "lapic entry len %d\n", ent->Length); 170 error = EINVAL; 171 } 172 break; 173 174 case ACPI_MADT_TYPE_IO_APIC: 175 if (ent->Length < sizeof(ACPI_MADT_IO_APIC)) { 176 kprintf("madt_iterate_entries: invalid MADT " 177 "ioapic entry len %d\n", ent->Length); 178 error = EINVAL; 179 } 180 break; 181 182 case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE: 183 if (ent->Length < sizeof(ACPI_MADT_INTERRUPT_OVERRIDE)) { 184 kprintf("madt_iterate_entries: invalid MADT " 185 "intsrc entry len %d\n", 186 ent->Length); 187 error = EINVAL; 188 } 189 break; 190 } 191 if (error) 192 break; 193 194 error = func(arg, ent); 195 if (error) 196 break; 197 198 ent = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, ent, ent->Length); 199 } 200 return error; 201 } 202 203 static int 204 madt_lapic_pass1_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 205 { 206 const ACPI_MADT_LOCAL_APIC_OVERRIDE *lapic_addr_ent; 207 uint64_t *addr64 = xarg; 208 209 if (ent->Type != ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE) 210 return 0; 211 if (ent->Length < sizeof(*lapic_addr_ent)) { 212 kprintf("madt_lapic_pass1: " 213 "invalid LAPIC address override length\n"); 214 return 0; 215 } 216 lapic_addr_ent = (const ACPI_MADT_LOCAL_APIC_OVERRIDE *)ent; 217 218 *addr64 = lapic_addr_ent->Address; 219 return 0; 220 } 221 222 static vm_paddr_t 223 madt_lapic_pass1(void) 224 { 225 ACPI_TABLE_MADT *madt; 226 vm_paddr_t lapic_addr; 227 uint64_t lapic_addr64; 228 int error; 229 230 KKASSERT(madt_phyaddr != 0); 231 232 madt = sdt_sdth_map(madt_phyaddr); 233 KKASSERT(madt != NULL); 234 235 MADT_VPRINTF("LAPIC address 0x%x, flags %#x\n", 236 madt->Address, madt->Flags); 237 lapic_addr = madt->Address; 238 239 lapic_addr64 = 0; 240 error = madt_iterate_entries(madt, madt_lapic_pass1_callback, 241 &lapic_addr64); 242 if (error) 243 panic("madt_iterate_entries(pass1) failed"); 244 245 if (lapic_addr64 != 0) { 246 kprintf("ACPI MADT: 64bits lapic address 0x%lx\n", 247 lapic_addr64); 248 lapic_addr = lapic_addr64; 249 } 250 251 sdt_sdth_unmap(&madt->Header); 252 253 return lapic_addr; 254 } 255 256 struct madt_lapic_pass2_cbarg { 257 int cpu; 258 int bsp_found; 259 int bsp_apic_id; 260 }; 261 262 static int 263 madt_lapic_pass2_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 264 { 265 const ACPI_MADT_LOCAL_APIC *lapic_ent; 266 struct madt_lapic_pass2_cbarg *arg = xarg; 267 268 if (ent->Type != ACPI_MADT_TYPE_LOCAL_APIC) 269 return 0; 270 271 lapic_ent = (const ACPI_MADT_LOCAL_APIC *)ent; 272 if (lapic_ent->LapicFlags & ACPI_MADT_ENABLED) { 273 MADT_VPRINTF("cpu id %d, apic id %d\n", 274 lapic_ent->ProcessorId, lapic_ent->Id); 275 if (lapic_ent->Id == arg->bsp_apic_id) { 276 lapic_set_cpuid(0, lapic_ent->Id); 277 arg->bsp_found = 1; 278 } else { 279 lapic_set_cpuid(arg->cpu, lapic_ent->Id); 280 arg->cpu++; 281 } 282 } 283 return 0; 284 } 285 286 static int 287 madt_lapic_pass2(int bsp_apic_id) 288 { 289 ACPI_TABLE_MADT *madt; 290 struct madt_lapic_pass2_cbarg arg; 291 int error; 292 293 MADT_VPRINTF("BSP apic id %d\n", bsp_apic_id); 294 295 KKASSERT(madt_phyaddr != 0); 296 297 madt = sdt_sdth_map(madt_phyaddr); 298 KKASSERT(madt != NULL); 299 300 bzero(&arg, sizeof(arg)); 301 arg.cpu = 1; 302 arg.bsp_apic_id = bsp_apic_id; 303 304 error = madt_iterate_entries(madt, madt_lapic_pass2_callback, &arg); 305 if (error) 306 panic("madt_iterate_entries(pass2) failed"); 307 308 KKASSERT(arg.bsp_found); 309 naps = arg.cpu - 1; /* exclude BSP */ 310 311 sdt_sdth_unmap(&madt->Header); 312 313 return 0; 314 } 315 316 struct madt_lapic_probe_cbarg { 317 int cpu_count; 318 vm_paddr_t lapic_addr; 319 }; 320 321 static int 322 madt_lapic_probe_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 323 { 324 struct madt_lapic_probe_cbarg *arg = xarg; 325 326 if (ent->Type == ACPI_MADT_TYPE_LOCAL_APIC) { 327 const ACPI_MADT_LOCAL_APIC *lapic_ent; 328 329 lapic_ent = (const ACPI_MADT_LOCAL_APIC *)ent; 330 if (lapic_ent->LapicFlags & ACPI_MADT_ENABLED) { 331 arg->cpu_count++; 332 if (lapic_ent->Id == APICID_MAX) { 333 kprintf("madt_lapic_probe: " 334 "invalid LAPIC apic id %d\n", 335 lapic_ent->Id); 336 return EINVAL; 337 } 338 } 339 } else if (ent->Type == ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE) { 340 const ACPI_MADT_LOCAL_APIC_OVERRIDE *lapic_addr_ent; 341 342 if (ent->Length < sizeof(*lapic_addr_ent)) { 343 kprintf("madt_lapic_probe: " 344 "invalid LAPIC address override length\n"); 345 return 0; 346 } 347 lapic_addr_ent = (const ACPI_MADT_LOCAL_APIC_OVERRIDE *)ent; 348 349 if (lapic_addr_ent->Address != 0) 350 arg->lapic_addr = lapic_addr_ent->Address; 351 } 352 return 0; 353 } 354 355 static int 356 madt_lapic_probe(struct lapic_enumerator *e) 357 { 358 struct madt_lapic_probe_cbarg arg; 359 ACPI_TABLE_MADT *madt; 360 int error; 361 362 if (madt_phyaddr == 0) 363 return ENXIO; 364 365 madt = sdt_sdth_map(madt_phyaddr); 366 KKASSERT(madt != NULL); 367 368 bzero(&arg, sizeof(arg)); 369 arg.lapic_addr = madt->Address; 370 371 error = madt_iterate_entries(madt, madt_lapic_probe_callback, &arg); 372 if (!error) { 373 if (arg.cpu_count == 0) { 374 kprintf("madt_lapic_probe: no CPU is found\n"); 375 error = EOPNOTSUPP; 376 } 377 if (arg.lapic_addr == 0) { 378 kprintf("madt_lapic_probe: zero LAPIC address\n"); 379 error = EOPNOTSUPP; 380 } 381 } 382 383 sdt_sdth_unmap(&madt->Header); 384 return error; 385 } 386 387 static int 388 madt_lapic_enumerate(struct lapic_enumerator *e) 389 { 390 vm_paddr_t lapic_addr; 391 int bsp_apic_id; 392 393 KKASSERT(madt_phyaddr != 0); 394 395 lapic_addr = madt_lapic_pass1(); 396 if (lapic_addr == 0) 397 panic("madt_lapic_enumerate: no local apic"); 398 399 lapic_map(lapic_addr); 400 401 bsp_apic_id = APIC_ID(lapic->id); 402 if (bsp_apic_id == APICID_MAX) { 403 /* 404 * XXX 405 * Some old brain dead BIOS will set BSP's LAPIC apic id 406 * to 255, though all LAPIC entries in MADT are valid. 407 */ 408 kprintf("%s invalid BSP LAPIC apic id %d\n", __func__, 409 bsp_apic_id); 410 return EINVAL; 411 } 412 413 if (madt_lapic_pass2(bsp_apic_id)) 414 panic("madt_lapic_enumerate: madt_lapic_pass2 failed"); 415 416 return 0; 417 } 418 419 static struct lapic_enumerator madt_lapic_enumerator = { 420 .lapic_prio = LAPIC_ENUM_PRIO_MADT, 421 .lapic_probe = madt_lapic_probe, 422 .lapic_enumerate = madt_lapic_enumerate 423 }; 424 425 static void 426 madt_lapic_enum_register(void) 427 { 428 int prio; 429 430 prio = LAPIC_ENUM_PRIO_MADT; 431 kgetenv_int("hw.madt_lapic_prio", &prio); 432 madt_lapic_enumerator.lapic_prio = prio; 433 434 lapic_enumerator_register(&madt_lapic_enumerator); 435 } 436 SYSINIT(madt_lapic, SI_BOOT2_PRESMP, SI_ORDER_ANY, madt_lapic_enum_register, 0); 437 438 struct madt_ioapic_probe_cbarg { 439 int ioapic_cnt; 440 int gsi_base0; 441 }; 442 443 static int 444 madt_ioapic_probe_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 445 { 446 struct madt_ioapic_probe_cbarg *arg = xarg; 447 448 if (ent->Type == ACPI_MADT_TYPE_INTERRUPT_OVERRIDE) { 449 const ACPI_MADT_INTERRUPT_OVERRIDE *intsrc_ent; 450 int trig, pola; 451 452 intsrc_ent = (const ACPI_MADT_INTERRUPT_OVERRIDE *)ent; 453 454 if (intsrc_ent->SourceIrq >= ISA_IRQ_CNT) { 455 kprintf("madt_ioapic_probe: invalid intsrc irq (%d)\n", 456 intsrc_ent->SourceIrq); 457 return EINVAL; 458 } 459 460 if (intsrc_ent->Bus != MADT_INT_BUS_ISA) { 461 kprintf("ACPI MADT: warning intsrc irq %d " 462 "bus is not ISA (%d)\n", 463 intsrc_ent->SourceIrq, intsrc_ent->Bus); 464 } 465 466 trig = intsrc_ent->IntiFlags & ACPI_MADT_TRIGGER_MASK; 467 if (trig == ACPI_MADT_TRIGGER_RESERVED) { 468 kprintf("ACPI MADT: warning invalid intsrc irq %d " 469 "trig, reserved\n", intsrc_ent->SourceIrq); 470 } else if (trig == ACPI_MADT_TRIGGER_LEVEL) { 471 MADT_VPRINTF("warning invalid intsrc irq %d " 472 "trig, level\n", intsrc_ent->SourceIrq); 473 } 474 475 pola = intsrc_ent->IntiFlags & ACPI_MADT_POLARITY_MASK; 476 if (pola == ACPI_MADT_POLARITY_RESERVED) { 477 kprintf("ACPI MADT: warning invalid intsrc irq %d " 478 "pola, reserved\n", intsrc_ent->SourceIrq); 479 } else if (pola == ACPI_MADT_POLARITY_ACTIVE_LOW) { 480 MADT_VPRINTF("warning invalid intsrc irq %d " 481 "pola, low\n", intsrc_ent->SourceIrq); 482 } 483 } else if (ent->Type == ACPI_MADT_TYPE_IO_APIC) { 484 const ACPI_MADT_IO_APIC *ioapic_ent; 485 486 ioapic_ent = (const ACPI_MADT_IO_APIC *)ent; 487 if (ioapic_ent->Address == 0) { 488 kprintf("madt_ioapic_probe: zero IOAPIC address\n"); 489 return EINVAL; 490 } 491 if (ioapic_ent->Id == APICID_MAX) { 492 kprintf("madt_ioapic_probe: " 493 "invalid IOAPIC apic id %d\n", 494 ioapic_ent->Id); 495 return EINVAL; 496 } 497 498 arg->ioapic_cnt++; 499 if (ioapic_ent->GlobalIrqBase == 0) 500 arg->gsi_base0 = 1; 501 } 502 return 0; 503 } 504 505 static int 506 madt_ioapic_probe(struct ioapic_enumerator *e) 507 { 508 struct madt_ioapic_probe_cbarg arg; 509 ACPI_TABLE_MADT *madt; 510 int error; 511 512 if (madt_phyaddr == 0) 513 return ENXIO; 514 515 madt = sdt_sdth_map(madt_phyaddr); 516 KKASSERT(madt != NULL); 517 518 bzero(&arg, sizeof(arg)); 519 520 error = madt_iterate_entries(madt, madt_ioapic_probe_callback, &arg); 521 if (!error) { 522 if (arg.ioapic_cnt == 0) { 523 kprintf("madt_ioapic_probe: no IOAPIC\n"); 524 error = ENXIO; 525 } 526 if (!arg.gsi_base0) { 527 kprintf("madt_ioapic_probe: no GSI base 0\n"); 528 error = EINVAL; 529 } 530 } 531 532 sdt_sdth_unmap(&madt->Header); 533 return error; 534 } 535 536 static int 537 madt_ioapic_enum_callback(void *xarg, const ACPI_SUBTABLE_HEADER *ent) 538 { 539 if (ent->Type == ACPI_MADT_TYPE_INTERRUPT_OVERRIDE) { 540 const ACPI_MADT_INTERRUPT_OVERRIDE *intsrc_ent; 541 enum intr_trigger trig; 542 enum intr_polarity pola; 543 int ent_trig, ent_pola; 544 545 intsrc_ent = (const ACPI_MADT_INTERRUPT_OVERRIDE *)ent; 546 547 KKASSERT(intsrc_ent->SourceIrq < ISA_IRQ_CNT); 548 if (intsrc_ent->Bus != MADT_INT_BUS_ISA) 549 return 0; 550 551 ent_trig = intsrc_ent->IntiFlags & ACPI_MADT_TRIGGER_MASK; 552 if (ent_trig == ACPI_MADT_TRIGGER_RESERVED) 553 return 0; 554 else if (ent_trig == ACPI_MADT_TRIGGER_LEVEL) 555 trig = INTR_TRIGGER_LEVEL; 556 else 557 trig = INTR_TRIGGER_EDGE; 558 559 ent_pola = intsrc_ent->IntiFlags & ACPI_MADT_POLARITY_MASK; 560 if (ent_pola == ACPI_MADT_POLARITY_RESERVED) 561 return 0; 562 else if (ent_pola == ACPI_MADT_POLARITY_ACTIVE_LOW) 563 pola = INTR_POLARITY_LOW; 564 else 565 pola = INTR_POLARITY_HIGH; 566 567 if (intsrc_ent->SourceIrq == acpi_sci_irqno()) { 568 acpi_sci_setmode1(trig, pola); 569 MADT_VPRINTF("SCI irq %d, first test %s/%s\n", 570 intsrc_ent->SourceIrq, 571 intr_str_trigger(trig), intr_str_polarity(pola)); 572 } 573 574 /* 575 * We ignore the polarity and trigger changes, since 576 * most of them are wrong or useless at best. 577 */ 578 if (intsrc_ent->SourceIrq == intsrc_ent->GlobalIrq) { 579 /* Nothing changed */ 580 return 0; 581 } 582 trig = INTR_TRIGGER_EDGE; 583 pola = INTR_POLARITY_HIGH; 584 585 MADT_VPRINTF("INTSRC irq %d -> gsi %u %s/%s\n", 586 intsrc_ent->SourceIrq, intsrc_ent->GlobalIrq, 587 intr_str_trigger(trig), intr_str_polarity(pola)); 588 ioapic_intsrc(intsrc_ent->SourceIrq, intsrc_ent->GlobalIrq, 589 trig, pola); 590 } else if (ent->Type == ACPI_MADT_TYPE_IO_APIC) { 591 const ACPI_MADT_IO_APIC *ioapic_ent; 592 uint32_t ver; 593 void *addr; 594 int npin; 595 596 ioapic_ent = (const ACPI_MADT_IO_APIC *)ent; 597 MADT_VPRINTF("IOAPIC addr 0x%08x, apic id %d, gsi base %u\n", 598 ioapic_ent->Address, ioapic_ent->Id, 599 ioapic_ent->GlobalIrqBase); 600 601 addr = ioapic_map(ioapic_ent->Address); 602 603 ver = ioapic_read(addr, IOAPIC_VER); 604 npin = ((ver & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1; 605 606 ioapic_add(addr, ioapic_ent->GlobalIrqBase, npin); 607 } 608 return 0; 609 } 610 611 static void 612 madt_ioapic_enumerate(struct ioapic_enumerator *e) 613 { 614 ACPI_TABLE_MADT *madt; 615 int error; 616 617 KKASSERT(madt_phyaddr != 0); 618 619 madt = sdt_sdth_map(madt_phyaddr); 620 KKASSERT(madt != NULL); 621 622 error = madt_iterate_entries(madt, madt_ioapic_enum_callback, NULL); 623 if (error) 624 panic("madt_ioapic_enumerate failed"); 625 626 sdt_sdth_unmap(&madt->Header); 627 } 628 629 static struct ioapic_enumerator madt_ioapic_enumerator = { 630 .ioapic_prio = IOAPIC_ENUM_PRIO_MADT, 631 .ioapic_probe = madt_ioapic_probe, 632 .ioapic_enumerate = madt_ioapic_enumerate 633 }; 634 635 static void 636 madt_ioapic_enum_register(void) 637 { 638 int prio; 639 640 prio = IOAPIC_ENUM_PRIO_MADT; 641 kgetenv_int("hw.madt_ioapic_prio", &prio); 642 madt_ioapic_enumerator.ioapic_prio = prio; 643 644 ioapic_enumerator_register(&madt_ioapic_enumerator); 645 } 646 SYSINIT(madt_ioapic, SI_BOOT2_PRESMP, SI_ORDER_ANY, 647 madt_ioapic_enum_register, 0); 648