Module Name: src Committed By: imil Date: Mon Jan 13 06:35:38 UTC 2025
Modified Files: src/sys/arch/x86/x86: mpbios.c Log Message: 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 /!\ This needs a new kernel option: MPTABLE_LINUX_BUG_COMPAT To generate a diff of this commit: cvs rdiff -u -r1.71 -r1.72 src/sys/arch/x86/x86/mpbios.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/x86/x86/mpbios.c diff -u src/sys/arch/x86/x86/mpbios.c:1.71 src/sys/arch/x86/x86/mpbios.c:1.72 --- src/sys/arch/x86/x86/mpbios.c:1.71 Thu Oct 7 12:52:27 2021 +++ src/sys/arch/x86/x86/mpbios.c Mon Jan 13 06:35:38 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: mpbios.c,v 1.71 2021/10/07 12:52:27 msaitoh Exp $ */ +/* $NetBSD: mpbios.c,v 1.72 2025/01/13 06:35:38 imil Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -96,7 +96,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: mpbios.c,v 1.71 2021/10/07 12:52:27 msaitoh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: mpbios.c,v 1.72 2025/01/13 06:35:38 imil Exp $"); #include "acpica.h" #include "lapic.h" @@ -209,6 +209,9 @@ static void mpbios_int(const uint8_t *, 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. * @@ -558,6 +599,9 @@ mpbios_scan(device_t self, int *ncpup) #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; const struct mpbios_int *iep; @@ -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)) {