FreeBSD/Linux Kernel Cross Reference
sys/i386/acpica/madt.c
1 /*-
2 * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD: releng/5.2/sys/i386/acpica/madt.c 123133 2003-12-03 20:33:18Z jhb $");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/smp.h>
39
40 #include <vm/vm.h>
41 #include <vm/vm_param.h>
42 #include <vm/pmap.h>
43
44 #include <machine/apicreg.h>
45 #include <machine/frame.h>
46 #include <machine/intr_machdep.h>
47 #include <machine/apicvar.h>
48 #include <machine/md_var.h>
49 #include <machine/specialreg.h>
50
51 #include "acpi.h"
52 #include <dev/acpica/acpivar.h>
53 #include <dev/acpica/madt.h>
54 #include <dev/pci/pcivar.h>
55
56 #define NIOAPICS 32 /* Max number of I/O APICs */
57 #define NLAPICS 32 /* Max number of local APICs */
58
59 typedef void madt_entry_handler(APIC_HEADER *entry, void *arg);
60
61 /* These two arrays are indexed by APIC IDs. */
62 struct ioapic_info {
63 void *io_apic;
64 UINT32 io_vector;
65 } ioapics[NIOAPICS];
66
67 struct lapic_info {
68 u_int la_present:1;
69 u_int la_enabled:1;
70 u_int la_apic_id:8;
71 } lapics[NLAPICS + 1];
72
73 static APIC_TABLE *madt;
74 static vm_paddr_t madt_physaddr;
75 static vm_offset_t madt_length;
76
77 MALLOC_DEFINE(M_MADT, "MADT Table", "ACPI MADT Table Items");
78
79 static u_char interrupt_polarity(UINT16 Polarity);
80 static u_char interrupt_trigger(UINT16 TriggerMode);
81 static int madt_find_cpu(u_int acpi_id, u_int *apic_id);
82 static int madt_find_interrupt(int intr, void **apic, u_int *pin);
83 static void *madt_map(vm_paddr_t pa, int offset, vm_offset_t length);
84 static void *madt_map_table(vm_paddr_t pa, int offset, const char *sig);
85 static void madt_parse_apics(APIC_HEADER *entry, void *arg);
86 static void madt_parse_interrupt_override(INTERRUPT_SOURCE_OVERRIDE *intr);
87 static void madt_parse_ints(APIC_HEADER *entry, void *arg __unused);
88 static void madt_parse_local_nmi(LAPIC_NMI *nmi);
89 static void madt_parse_nmi(NMI *nmi);
90 static int madt_probe(void);
91 static int madt_probe_cpus(void);
92 static void madt_probe_cpus_handler(APIC_HEADER *entry, void *arg __unused);
93 static int madt_probe_table(vm_paddr_t address);
94 static void madt_register(void *dummy);
95 static int madt_setup_local(void);
96 static int madt_setup_io(void);
97 static void madt_unmap(void *data, vm_offset_t length);
98 static void madt_unmap_table(void *table);
99 static void madt_walk_table(madt_entry_handler *handler, void *arg);
100
101 static struct apic_enumerator madt_enumerator = {
102 "MADT",
103 madt_probe,
104 madt_probe_cpus,
105 madt_setup_local,
106 madt_setup_io
107 };
108
109 /*
110 * Code to abuse the crashdump map to map in the tables for the early
111 * probe. We cheat and make the following assumptions about how we
112 * use this KVA: page 0 is used to map in the first page of each table
113 * found via the RSDT or XSDT and pages 1 to n are used to map in the
114 * RSDT or XSDT. The offset is in pages; the length is in bytes.
115 */
116 static void *
117 madt_map(vm_paddr_t pa, int offset, vm_offset_t length)
118 {
119 vm_offset_t va, off;
120 void *data;
121
122 off = pa & PAGE_MASK;
123 length = roundup(length + off, PAGE_SIZE);
124 pa = pa & PG_FRAME;
125 va = (vm_offset_t)pmap_kenter_temporary(pa, offset) +
126 (offset * PAGE_SIZE);
127 data = (void *)(va + off);
128 length -= PAGE_SIZE;
129 while (length > 0) {
130 va += PAGE_SIZE;
131 pa += PAGE_SIZE;
132 length -= PAGE_SIZE;
133 pmap_kenter(va, pa);
134 invlpg(va);
135 }
136 return (data);
137 }
138
139 static void
140 madt_unmap(void *data, vm_offset_t length)
141 {
142 vm_offset_t va, off;
143
144 va = (vm_offset_t)data;
145 off = va & PAGE_MASK;
146 length = roundup(length + off, PAGE_SIZE);
147 va &= ~PAGE_MASK;
148 while (length > 0) {
149 pmap_kremove(va);
150 invlpg(va);
151 va += PAGE_SIZE;
152 length -= PAGE_SIZE;
153 }
154 }
155
156 static void *
157 madt_map_table(vm_paddr_t pa, int offset, const char *sig)
158 {
159 ACPI_TABLE_HEADER *header;
160 vm_offset_t length;
161
162 header = madt_map(pa, offset, sizeof(ACPI_TABLE_HEADER));
163 if (strncmp(header->Signature, sig, 4) != 0) {
164 madt_unmap(header, sizeof(ACPI_TABLE_HEADER));
165 return (NULL);
166 }
167 length = header->Length;
168 madt_unmap(header, sizeof(ACPI_TABLE_HEADER));
169 return (madt_map(pa, offset, length));
170 }
171
172 static void
173 madt_unmap_table(void *table)
174 {
175 ACPI_TABLE_HEADER *header;
176
177 header = (ACPI_TABLE_HEADER *)table;
178 madt_unmap(table, header->Length);
179 }
180
181 /*
182 * Look for an ACPI Multiple APIC Description Table ("APIC")
183 */
184 static int
185 madt_probe(void)
186 {
187 ACPI_POINTER rsdp_ptr;
188 RSDP_DESCRIPTOR *rsdp;
189 RSDT_DESCRIPTOR *rsdt;
190 XSDT_DESCRIPTOR *xsdt;
191 int i, count;
192
193 if (resource_disabled("acpi", 0))
194 return (ENXIO);
195
196 /*
197 * Map in the RSDP. Since ACPI uses AcpiOsMapMemory() which in turn
198 * calls pmap_mapdev() to find the RSDP, we assume that we can use
199 * pmap_mapdev() to map the RSDP.
200 */
201 if (AcpiOsGetRootPointer(ACPI_LOGICAL_ADDRESSING, &rsdp_ptr) != AE_OK)
202 return (ENXIO);
203 #ifdef __i386__
204 KASSERT(rsdp_ptr.Pointer.Physical < KERNLOAD, ("RSDP too high"));
205 #endif
206 rsdp = pmap_mapdev(rsdp_ptr.Pointer.Physical, sizeof(RSDP_DESCRIPTOR));
207 if (rsdp == NULL) {
208 if (bootverbose)
209 printf("MADT: Failed to map RSDP\n");
210 return (ENXIO);
211 }
212
213 /*
214 * For ACPI < 2.0, use the RSDT. For ACPI >= 2.0, use the XSDT.
215 * We map the XSDT and RSDT at page 1 in the crashdump area.
216 * Page 0 is used to map in the headers of candidate ACPI tables.
217 */
218 if (rsdp->Revision >= 2) {
219 xsdt = madt_map_table(rsdp->XsdtPhysicalAddress, 1, XSDT_SIG);
220 if (xsdt == NULL) {
221 if (bootverbose)
222 printf("MADT: Failed to map XSDT\n");
223 return (ENXIO);
224 }
225 count = (xsdt->Header.Length - sizeof(ACPI_TABLE_HEADER)) /
226 sizeof(UINT64);
227 for (i = 0; i < count; i++)
228 if (madt_probe_table(xsdt->TableOffsetEntry[i]))
229 break;
230 madt_unmap_table(xsdt);
231 } else {
232 rsdt = madt_map_table(rsdp->RsdtPhysicalAddress, 1, RSDT_SIG);
233 if (rsdt == NULL) {
234 if (bootverbose)
235 printf("MADT: Failed to map RSDT\n");
236 return (ENXIO);
237 }
238 count = (rsdt->Header.Length - sizeof(ACPI_TABLE_HEADER)) /
239 sizeof(UINT32);
240 for (i = 0; i < count; i++)
241 if (madt_probe_table(rsdt->TableOffsetEntry[i]))
242 break;
243 madt_unmap_table(rsdt);
244 }
245 pmap_unmapdev((vm_offset_t)rsdp, sizeof(RSDP_DESCRIPTOR));
246 if (madt_physaddr == 0) {
247 if (bootverbose)
248 printf("MADT: No MADT table found\n");
249 return (ENXIO);
250 }
251 if (bootverbose)
252 printf("MADT: Found table at 0x%jx\n",
253 (uintmax_t)madt_physaddr);
254
255 return (0);
256 }
257
258 /*
259 * See if a given ACPI table is the MADT.
260 */
261 static int
262 madt_probe_table(vm_paddr_t address)
263 {
264 ACPI_TABLE_HEADER *table;
265
266 table = madt_map(address, 0, sizeof(ACPI_TABLE_HEADER));
267 if (table == NULL) {
268 if (bootverbose)
269 printf("MADT: Failed to map table at 0x%jx\n",
270 (uintmax_t)address);
271 return (0);
272 }
273 if (bootverbose)
274 printf("Table '%.4s' at 0x%jx\n", table->Signature,
275 (uintmax_t)address);
276
277 /* XXX: Verify checksum? */
278 if (strncmp(table->Signature, APIC_SIG, 4) != 0) {
279 madt_unmap(table, sizeof(ACPI_TABLE_HEADER));
280 return (0);
281 }
282 madt_physaddr = address;
283 madt_length = table->Length;
284 madt_unmap(table, sizeof(ACPI_TABLE_HEADER));
285 return (1);
286 }
287
288 /*
289 * Run through the MP table enumerating CPUs.
290 */
291 static int
292 madt_probe_cpus(void)
293 {
294
295 madt = madt_map_table(madt_physaddr, 0, APIC_SIG);
296 KASSERT(madt != NULL, ("Unable to re-map MADT"));
297 madt_walk_table(madt_probe_cpus_handler, NULL);
298 madt_unmap_table(madt);
299 madt = NULL;
300 return (0);
301 }
302
303 /*
304 * Initialize the local APIC on the BSP.
305 */
306 static int
307 madt_setup_local(void)
308 {
309
310 madt = pmap_mapdev(madt_physaddr, madt_length);
311 lapic_init((uintptr_t)madt->LocalApicAddress);
312 printf("ACPI APIC Table: <%.*s %.*s>\n",
313 (int)sizeof(madt->Header.OemId), madt->Header.OemId,
314 (int)sizeof(madt->Header.OemTableId), madt->Header.OemTableId);
315
316 /*
317 * We ignore 64-bit local APIC override entries. Should we
318 * perhaps emit a warning here if we find one?
319 */
320 return (0);
321 }
322
323 /*
324 * Run through the MP table enumerating I/O APICs.
325 */
326 static int
327 madt_setup_io(void)
328 {
329 int i;
330
331 /* First, we run through adding I/O APIC's. */
332 madt_walk_table(madt_parse_apics, NULL);
333
334 /* Second, we run through the table tweaking interrupt sources. */
335 madt_walk_table(madt_parse_ints, NULL);
336
337 /* Third, we register all the I/O APIC's. */
338 for (i = 0; i < NIOAPICS; i++)
339 if (ioapics[i].io_apic != NULL)
340 ioapic_register(ioapics[i].io_apic);
341
342 /* Finally, we throw the switch to enable the I/O APIC's. */
343 acpi_SetDefaultIntrModel(ACPI_INTR_APIC);
344
345 return (0);
346 }
347
348 static void
349 madt_register(void *dummy __unused)
350 {
351
352 apic_register_enumerator(&madt_enumerator);
353 }
354 SYSINIT(madt_register, SI_SUB_CPU - 1, SI_ORDER_FIRST, madt_register, NULL)
355
356 /*
357 * Call the handler routine for each entry in the MADT table.
358 */
359 static void
360 madt_walk_table(madt_entry_handler *handler, void *arg)
361 {
362 APIC_HEADER *entry;
363 u_char *p, *end;
364
365 end = (u_char *)(madt) + madt->Header.Length;
366 for (p = (u_char *)(madt + 1); p < end; ) {
367 entry = (APIC_HEADER *)p;
368 handler(entry, arg);
369 p += entry->Length;
370 }
371 }
372
373 static void
374 madt_probe_cpus_handler(APIC_HEADER *entry, void *arg)
375 {
376 PROCESSOR_APIC *proc;
377 struct lapic_info *la;
378
379 switch (entry->Type) {
380 case APIC_PROC:
381 /*
382 * The MADT does not include a BSP flag, so we have to
383 * let the MP code figure out which CPU is the BSP on
384 * its own.
385 */
386 proc = (PROCESSOR_APIC *)entry;
387 if (bootverbose)
388 printf("MADT: Found CPU APIC ID %d ACPI ID %d: %s\n",
389 proc->LocalApicId, proc->ProcessorApicId,
390 proc->ProcessorEnabled ? "enabled" : "disabled");
391 if (proc->ProcessorApicId > NLAPICS)
392 panic("%s: CPU ID %d too high", __func__,
393 proc->ProcessorApicId);
394 la = &lapics[proc->ProcessorApicId];
395 KASSERT(la->la_present == 0,
396 ("Duplicate local ACPI ID %d", proc->ProcessorApicId));
397 la->la_present = 1;
398 la->la_apic_id = proc->LocalApicId;
399 if (proc->ProcessorEnabled) {
400 la->la_enabled = 1;
401 lapic_create(proc->LocalApicId, 0);
402 }
403 break;
404 }
405 }
406
407
408 /*
409 * Add an I/O APIC from an entry in the table.
410 */
411 static void
412 madt_parse_apics(APIC_HEADER *entry, void *arg __unused)
413 {
414 IO_APIC *apic;
415
416
417 switch (entry->Type) {
418 case APIC_IO:
419 apic = (IO_APIC *)entry;
420 if (bootverbose)
421 printf("MADT: Found IO APIC ID %d, Vector %d at %p\n",
422 apic->IoApicId, apic->Vector,
423 (void *)(uintptr_t)apic->IoApicAddress);
424 if (apic->IoApicId >= NIOAPICS)
425 panic("%s: I/O APIC ID %d too high", __func__,
426 apic->IoApicId);
427 if (ioapics[apic->IoApicId].io_apic != NULL)
428 panic("%s: Double APIC ID %d", __func__,
429 apic->IoApicId);
430 ioapics[apic->IoApicId].io_apic = ioapic_create(
431 (uintptr_t)apic->IoApicAddress, apic->IoApicId,
432 apic->Vector);
433 ioapics[apic->IoApicId].io_vector = apic->Vector;
434 break;
435 default:
436 break;
437 }
438 }
439
440 /*
441 * Determine properties of an interrupt source. Note that for ACPI,
442 * these are only used for ISA interrupts, so we assume ISA bus values
443 * (Active Hi, Edge Triggered) for conforming values.
444 */
445 static u_char
446 interrupt_polarity(UINT16 Polarity)
447 {
448
449 switch (Polarity) {
450 case APIC_POLARITY_CONFORM:
451 case APIC_POLARITY_ACTIVEHI:
452 return (1);
453 case APIC_POLARITY_ACTIVELO:
454 return (0);
455 default:
456 panic("Bogus Interrupt Polarity");
457 }
458 }
459
460 static u_char
461 interrupt_trigger(UINT16 TriggerMode)
462 {
463
464 switch (TriggerMode) {
465 case APIC_TRIGGER_CONFORM:
466 case APIC_TRIGGER_EDGE:
467 return (1);
468 case APIC_TRIGGER_LEVEL:
469 return (0);
470 default:
471 panic("Bogus Interrupt Trigger Mode");
472 }
473 }
474
475 /*
476 * Find the local APIC ID associated with a given ACPI Processor ID.
477 */
478 static int
479 madt_find_cpu(u_int acpi_id, u_int *apic_id)
480 {
481
482 if (!lapics[acpi_id].la_present)
483 return (ENOENT);
484 *apic_id = lapics[acpi_id].la_apic_id;
485 if (lapics[acpi_id].la_enabled)
486 return (0);
487 else
488 return (ENXIO);
489 }
490
491 /*
492 * Find the IO APIC and pin on that APIC associated with a given global
493 * interrupt.
494 */
495 static int
496 madt_find_interrupt(int intr, void **apic, u_int *pin)
497 {
498 int i, best;
499
500 best = -1;
501 for (i = 0; i < NIOAPICS; i++) {
502 if (ioapics[i].io_apic == NULL ||
503 ioapics[i].io_vector > intr)
504 continue;
505 if (best == -1 ||
506 ioapics[best].io_vector < ioapics[i].io_vector)
507 best = i;
508 }
509 if (best == -1)
510 return (ENOENT);
511 *apic = ioapics[best].io_apic;
512 *pin = intr - ioapics[best].io_vector;
513 if (*pin > 32)
514 printf("WARNING: Found intpin of %u for vector %d\n", *pin,
515 intr);
516 return (0);
517 }
518
519 /*
520 * Parse an interrupt source override for an ISA interrupt.
521 */
522 static void
523 madt_parse_interrupt_override(INTERRUPT_SOURCE_OVERRIDE *intr)
524 {
525 void *new_ioapic, *old_ioapic;
526 u_int new_pin, old_pin;
527
528 if (bootverbose)
529 printf("MADT: intr override: source %u, irq %u\n",
530 intr->Source, intr->GlobalSystemInterrupt);
531 KASSERT(intr->Bus == 0, ("bus for interrupt overrides must be zero"));
532 if (madt_find_interrupt(intr->GlobalSystemInterrupt, &new_ioapic,
533 &new_pin) != 0) {
534 printf("MADT: Could not find APIC for vector %d (IRQ %d)\n",
535 intr->GlobalSystemInterrupt, intr->Source);
536 return;
537 }
538
539 if (intr->Source != intr->GlobalSystemInterrupt) {
540 /* XXX: This assumes that the SCI uses IRQ 9. */
541 if (intr->GlobalSystemInterrupt > 15 && intr->Source == 9)
542 acpi_OverrideInterruptLevel(
543 intr->GlobalSystemInterrupt);
544 else
545 ioapic_remap_vector(new_ioapic, new_pin, intr->Source);
546 if (madt_find_interrupt(intr->Source, &old_ioapic,
547 &old_pin) != 0)
548 printf("MADT: Could not find APIC for source IRQ %d\n",
549 intr->Source);
550 else if (ioapic_get_vector(old_ioapic, old_pin) ==
551 intr->Source)
552 ioapic_disable_pin(old_ioapic, old_pin);
553 }
554 ioapic_set_triggermode(new_ioapic, new_pin,
555 interrupt_trigger(intr->TriggerMode));
556 ioapic_set_polarity(new_ioapic, new_pin,
557 interrupt_polarity(intr->Polarity));
558 }
559
560 /*
561 * Parse an entry for an NMI routed to an IO APIC.
562 */
563 static void
564 madt_parse_nmi(NMI *nmi)
565 {
566 void *ioapic;
567 u_int pin;
568
569 if (madt_find_interrupt(nmi->GlobalSystemInterrupt,
570 &ioapic, &pin) != 0) {
571 printf("MADT: Could not find APIC for vector %d\n",
572 nmi->GlobalSystemInterrupt);
573 return;
574 }
575
576 ioapic_set_nmi(ioapic, pin);
577 if (nmi->TriggerMode != APIC_TRIGGER_CONFORM)
578 ioapic_set_triggermode(ioapic, pin,
579 interrupt_trigger(nmi->TriggerMode));
580 if (nmi->Polarity != APIC_TRIGGER_CONFORM)
581 ioapic_set_polarity(ioapic, pin,
582 interrupt_polarity(nmi->Polarity));
583 }
584
585 /*
586 * Parse an entry for an NMI routed to a local APIC LVT pin.
587 */
588 static void
589 madt_parse_local_nmi(LAPIC_NMI *nmi)
590 {
591 u_int apic_id, pin;
592
593 if (nmi->ProcessorApicId == 0xff)
594 apic_id = APIC_ID_ALL;
595 else if (madt_find_cpu(nmi->ProcessorApicId, &apic_id) != 0) {
596 if (bootverbose)
597 printf("MADT: Ignoring local NMI routed to ACPI CPU %u\n",
598 nmi->ProcessorApicId);
599 return;
600 }
601 if (nmi->LINTPin == 0)
602 pin = LVT_LINT0;
603 else
604 pin = LVT_LINT1;
605 lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_NMI);
606 if (nmi->TriggerMode != APIC_TRIGGER_CONFORM)
607 lapic_set_lvt_triggermode(apic_id, pin,
608 interrupt_trigger(nmi->TriggerMode));
609 if (nmi->Polarity != APIC_POLARITY_CONFORM)
610 lapic_set_lvt_polarity(apic_id, pin,
611 interrupt_polarity(nmi->Polarity));
612 }
613
614 /*
615 * Parse interrupt entries.
616 */
617 static void
618 madt_parse_ints(APIC_HEADER *entry, void *arg __unused)
619 {
620
621 switch (entry->Type) {
622 case APIC_INTERRUPT_SOURCE_OVERRIDE:
623 madt_parse_interrupt_override(
624 (INTERRUPT_SOURCE_OVERRIDE *)entry);
625 break;
626 case APIC_NMI:
627 madt_parse_nmi((NMI *)entry);
628 break;
629 case APIC_LOCAL_APIC_NMI:
630 madt_parse_local_nmi((LAPIC_NMI *)entry);
631 break;
632 }
633 }
634
635 /*
636 * Setup per-CPU ACPI IDs.
637 */
638 static void
639 madt_set_ids(void *dummy)
640 {
641 struct pcpu *pc;
642 u_int i, j;
643
644 if (madt == NULL)
645 return;
646 for (i = 0; i <= mp_maxid; i++) {
647 if (CPU_ABSENT(i))
648 continue;
649 pc = pcpu_find(i);
650 KASSERT(pc != NULL, ("no pcpu data for CPU %d", i));
651 for (j = 0; j < NLAPICS + 1; j++) {
652 if (!lapics[j].la_present || !lapics[j].la_enabled)
653 continue;
654 if (lapics[j].la_apic_id == pc->pc_apic_id) {
655 pc->pc_acpi_id = j;
656 if (bootverbose)
657 printf("APIC: CPU %u has ACPI ID %u\n",
658 i, j);
659 break;
660 }
661 }
662 if (j == NLAPICS + 1)
663 panic("Unable to find ACPI ID for CPU %d", i);
664 }
665 }
666 SYSINIT(madt_set_ids, SI_SUB_CPU, SI_ORDER_ANY, madt_set_ids, NULL)
Cache object: c9f027c2f219a917ea9f16bede0cd54d
|