diff options
author | tegge <tegge@FreeBSD.org> | 2000-08-06 00:04:03 +0000 |
---|---|---|
committer | tegge <tegge@FreeBSD.org> | 2000-08-06 00:04:03 +0000 |
commit | a83353f91aca54c41c4c861b026dd2e1c2a95cbe (patch) | |
tree | c658601adae58dd16d10f4d10628de7a07fed3e9 /sys/kern | |
parent | 38baa3d84afacf843e0afcd466775444d5ef58a0 (diff) | |
download | FreeBSD-src-a83353f91aca54c41c4c861b026dd2e1c2a95cbe.zip FreeBSD-src-a83353f91aca54c41c4c861b026dd2e1c2a95cbe.tar.gz |
Be more verbose when changing APIC ID on an IO APIC.
Don't allow cpu entries in the MP table to contain APIC IDs out of range.
Don't write outside array boundaries if an IO APIC entry in the MP table
contains an APIC ID out of range.
Assign APIC IDs for all IO APICs according to section 3.6.6 in the
Intel MP spec:
- If the current APIC ID on an IO APIC doesn't conflict with other
IO APICs or CPUs, that APIC ID should be used. The copy of the MP
table must be updated if the corresponding APIC ID in the MP table
is different.
- If the current APIC ID was in conflict with other units, the
corresponding APIC ID specified in the MP table is checked for conflict.
- If a conflict is still found then fall back to using a new unique ID.
The copy of the MP table must be updated.
- IDs out of range is considered to be in conflict.
During these operations, the IO_TO_ID array cannot be used, since any
conflict would have caused information loss. The array is then corrected,
since all APIC ID conflicts should have been resolved.
PR: 20312, 18919
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/subr_smp.c | 144 |
1 files changed, 142 insertions, 2 deletions
diff --git a/sys/kern/subr_smp.c b/sys/kern/subr_smp.c index 1155e67..f8e77a1 100644 --- a/sys/kern/subr_smp.c +++ b/sys/kern/subr_smp.c @@ -1013,6 +1013,105 @@ revoke_apic_irq(int irq) } } + + +static void +swap_apic_id(int apic, int oldid, int newid) +{ + int x; + int oapic; + + + if (oldid == newid) + return; /* Nothing to do */ + + printf("Changing APIC ID for IO APIC #%d from %d to %d in MP table\n", + apic, oldid, newid); + + /* Swap physical APIC IDs in interrupt entries */ + for (x = 0; x < nintrs; x++) { + if (io_apic_ints[x].dst_apic_id == oldid) + io_apic_ints[x].dst_apic_id = newid; + else if (io_apic_ints[x].dst_apic_id == newid) + io_apic_ints[x].dst_apic_id = oldid; + } + + /* Swap physical APIC IDs in IO_TO_ID mappings */ + for (oapic = 0; oapic < mp_napics; oapic++) + if (IO_TO_ID(oapic) == newid) + break; + + if (oapic < mp_napics) { + printf("Changing APIC ID for IO APIC #%d from " + "%d to %d in MP table\n", + oapic, newid, oldid); + IO_TO_ID(oapic) = oldid; + } + IO_TO_ID(apic) = newid; +} + + +static void +fix_id_to_io_mapping(void) +{ + int x; + + for (x = 0; x < NAPICID; x++) + ID_TO_IO(x) = -1; + + for (x = 0; x <= mp_naps; x++) + if (CPU_TO_ID(x) < NAPICID) + ID_TO_IO(CPU_TO_ID(x)) = x; + + for (x = 0; x < mp_napics; x++) + if (IO_TO_ID(x) < NAPICID) + ID_TO_IO(IO_TO_ID(x)) = x; +} + + +static int +first_free_apic_id(void) +{ + int freeid, x; + + for (freeid = 0; freeid < NAPICID; freeid++) { + for (x = 0; x <= mp_naps; x++) + if (CPU_TO_ID(x) == freeid) + break; + if (x <= mp_naps) + continue; + for (x = 0; x < mp_napics; x++) + if (IO_TO_ID(x) == freeid) + break; + if (x < mp_napics) + continue; + return freeid; + } + return freeid; +} + + +static int +io_apic_id_acceptable(int apic, int id) +{ + int cpu; /* Logical CPU number */ + int oapic; /* Logical IO APIC number for other IO APIC */ + + if (id >= NAPICID) + return 0; /* Out of range */ + + for (cpu = 0; cpu <= mp_naps; cpu++) + if (CPU_TO_ID(cpu) == id) + return 0; /* Conflict with CPU */ + + for (oapic = 0; oapic < mp_napics && oapic < apic; oapic++) + if (IO_TO_ID(oapic) == id) + return 0; /* Conflict with other APIC */ + + return 1; /* ID is acceptable for IO APIC */ +} + + /* * parse an Intel MP specification table */ @@ -1024,6 +1123,9 @@ fix_mp_table(void) int bus_0 = 0; /* Stop GCC warning */ int bus_pci = 0; /* Stop GCC warning */ int num_pci_bus; + int apic; /* IO APIC unit number */ + int freeid; /* Free physical APIC ID */ + int physid; /* Current physical IO APIC ID */ /* * Fix mis-numbering of the PCI bus and its INT entries if the BIOS @@ -1076,6 +1178,42 @@ fix_mp_table(void) } } } + + /* Assign IO APIC IDs. + * + * First try the existing ID. If a conflict is detected, try + * the ID in the MP table. If a conflict is still detected, find + * a free id. + * + * We cannot use the ID_TO_IO table before all conflicts has been + * resolved and the table has been corrected. + */ + for (apic = 0; apic < mp_napics; ++apic) { /* For all IO APICs */ + + /* First try to use the value set by the BIOS */ + physid = io_apic_get_id(apic); + if (io_apic_id_acceptable(apic, physid)) { + if (IO_TO_ID(apic) != physid) + swap_apic_id(apic, IO_TO_ID(apic), physid); + continue; + } + + /* Then check if the value in the MP table is acceptable */ + if (io_apic_id_acceptable(apic, IO_TO_ID(apic))) + continue; + + /* Last resort, find a free APIC ID and use it */ + freeid = first_free_apic_id(); + if (freeid >= NAPICID) + panic("No free physical APIC IDs found"); + + if (io_apic_id_acceptable(apic, freeid)) { + swap_apic_id(apic, IO_TO_ID(apic), freeid); + continue; + } + panic("Free physical APIC ID not usable"); + } + fix_id_to_io_mapping(); } @@ -1159,6 +1297,8 @@ processor_entry(proc_entry_ptr entry, int cpu) if (!(entry->cpu_flags & PROCENTRY_FLAG_EN)) return 0; + if(entry->apic_id >= NAPICID) + panic("CPU APIC ID out of range (0..%d)", NAPICID - 1); /* check for BSP flag */ if (entry->cpu_flags & PROCENTRY_FLAG_BP) { boot_cpu_id = entry->apic_id; @@ -1209,7 +1349,8 @@ io_apic_entry(io_apic_entry_ptr entry, int apic) return 0; IO_TO_ID(apic) = entry->apic_id; - ID_TO_IO(entry->apic_id) = apic; + if (entry->apic_id < NAPICID) + ID_TO_IO(entry->apic_id) = apic; return 1; } @@ -1602,7 +1743,6 @@ default_mp_table(int type) { int ap_cpu_id; #if defined(APIC_IO) - u_int32_t ux; int io_apic_id; int pin; #endif /* APIC_IO */ |