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

Reply via email to