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)) {