96501ade23a80cacb106325add64a0d1d7a16b1b Firecracker and qemu/microvm in MMIO mode don't have ACPI, either they rely on MP tables, but using it IOAPIC was not detected. This patch fixes it by adding a Linux-specific behavior, counting the right amount of entries and then find the IOAPIC entry. These bugs were found by Colin Percival and described here https://www.usenix.org/publications/loginonline/freebsd-firecracker diff --git a/sys/arch/x86/x86/mpbios.c b/sys/arch/x86/x86/mpbios.c index a3565a44251..3964975e0be 100644 --- a/sys/arch/x86/x86/mpbios.c +++ b/sys/arch/x86/x86/mpbios.c @@ -209,6 +209,9 @@ static void mpbios_int(const uint8_t *, int, struct mp_intr_map *); static const void *mpbios_map(paddr_t, int, struct mp_map *); static void mpbios_unmap(struct mp_map *); +#ifdef MPTABLE_LINUX_BUG_COMPAT +static uint16_t compute_entry_count(const uint8_t *, const uint8_t *); +#endif /* * globals to help us bounce our way through parsing the config table. */ @@ -333,6 +336,19 @@ mpbios_probe(device_t self) if (mp_fps != NULL) 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. + */ + mp_fps = mpbios_search(self, 639 * 1024, 1024 / 4, &mp_fp_map); + if (mp_fps != NULL) + goto found; +#endif + + /* nothing found */ return 0; @@ -533,6 +549,31 @@ static const uint8_t dflt_lint_tab[2] = { }; +#ifdef MPTABLE_LINUX_BUG_COMPAT +/* Compute the correct entry_count value. */ +static uint16_t +compute_entry_count(const uint8_t *entry, const uint8_t *end) +{ + size_t nentries = 0; + + while (entry < end) { + switch (*entry) { + case MPS_MCT_CPU: + case MPS_MCT_BUS: + case MPS_MCT_IOAPIC: + case MPS_MCT_IOINT: + case MPS_MCT_LINT: + break; + default: + panic("%s: Unknown MP Config Entry %d\n", __func__, + (int)*entry); + } + entry += mp_conf[*entry].length;; + nentries++; + } + return (uint16_t)(nentries); +} +#endif /* * 1st pass on BIOS's Intel MP specification table. * @@ -557,6 +598,9 @@ mpbios_scan(device_t self, int *ncpup) int intr_cnt, cur_intr; #if NLAPIC > 0 paddr_t lapic_base; +#endif +#ifdef MPTABLE_LINUX_BUG_COMPAT + uint16_t countfix = 0; #endif const struct dflt_conf_entry *dflt_conf; const int *dflt_bus_irq; @@ -677,6 +721,13 @@ mpbios_scan(device_t self, int *ncpup) position += sizeof(*mp_cth); count = mp_cth->entry_count; +#ifdef MPTABLE_LINUX_BUG_COMPAT + if (count == 0) { + /* count the correct entry_count */ + countfix = compute_entry_count(position, end); + count = countfix; + } +#endif intr_cnt = 0; while ((count--) && (position < end)) { @@ -721,6 +772,10 @@ mpbios_scan(device_t self, int *ncpup) /* re-walk the table, recording info of interest */ position = (const uint8_t *)mp_cth + sizeof(*mp_cth); count = mp_cth->entry_count; +#ifdef MPTABLE_LINUX_BUG_COMPAT + if (count == 0) + count = countfix; +#endif cur_intr = 0; while ((count--) && (position < end)) {