diff --git a/sys/conf/options.amd64 b/sys/conf/options.amd64 --- a/sys/conf/options.amd64 +++ b/sys/conf/options.amd64 @@ -63,3 +63,6 @@ # Don't create a "legacy" PCI bridge if none is found. NO_LEGACY_PCIB opt_cpu.h + +# Compatibility with Linux MP table bugs. +MPTABLE_LINUX_BUG_COMPAT diff --git a/sys/conf/options.i386 b/sys/conf/options.i386 --- a/sys/conf/options.i386 +++ b/sys/conf/options.i386 @@ -107,3 +107,6 @@ # Don't create a "legacy" PCI bridge if none is found. NO_LEGACY_PCIB opt_cpu.h + +# Compatibility with Linux MP table bugs. +MPTABLE_LINUX_BUG_COMPAT diff --git a/sys/x86/x86/mptable.c b/sys/x86/x86/mptable.c --- a/sys/x86/x86/mptable.c +++ b/sys/x86/x86/mptable.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include "opt_mptable_force_htt.h" +#include "opt_mptable_linux_bug_compat.h" #include #include #include @@ -245,6 +246,34 @@ return (UNKNOWN_BUSTYPE); } +#ifdef MPTABLE_LINUX_BUG_COMPAT +/* Compute the correct entry_count value. */ +static void +compute_entry_count(void) +{ + u_char *end = (u_char *)(mpct) + mpct->base_table_length; + u_char *entry = (u_char *)(mpct + 1); + size_t nentries = 0; + + while (entry < end) { + switch (*entry) { + case MPCT_ENTRY_PROCESSOR: + case MPCT_ENTRY_IOAPIC: + case MPCT_ENTRY_BUS: + case MPCT_ENTRY_INT: + case MPCT_ENTRY_LOCAL_INT: + break; + default: + panic("%s: Unknown MP Config Entry %d\n", __func__, + (int)*entry); + } + entry += basetable_entry_types[*entry].length; + nentries++; + } + mpct->entry_count = (uint16_t)(nentries); +} +#endif + /* * Look for an Intel MP spec table (ie, SMP capable hardware). */ @@ -273,6 +302,17 @@ if ((x = search_for_sig(target, BIOS_COUNT)) >= 0) goto found; +#ifdef MPTABLE_LINUX_BUG_COMPAT + /* + * Linux assumes that it always has 640 kB of base memory and + * searches for the MP table at 639k regardless of whether that + * address is present in the system memory map. Some VM systems + * rely on this buggy behaviour. + */ + if ((x = search_for_sig(639 * 1024, 1024 / 4)) >= 0) + goto found; +#endif + /* nothing found */ return (ENXIO); @@ -321,6 +361,16 @@ printf( "MP Configuration Table version 1.%d found at %p\n", mpct->spec_rev, mpct); +#ifdef MPTABLE_LINUX_BUG_COMPAT + /* + * Linux ignores entry_count and instead scans the MP table + * until it runs out of bytes of table (as specified by the + * base_table_length field). Some VM systems rely on this + * buggy behaviour and record an entry_count of zero. + */ + if (mpct->entry_count == 0) + compute_entry_count(); +#endif } return (-100);