[RFC PATCH 10/18] aarch64: route bootstrap modules through kern/bootstrap.c

Paulo Duarte <[email protected]>
Newsgroups gmane.os.hurd.bugs
Message-ID <[email protected]>
Bugaev's wip-aarch64 carried an aarch64-side rewrite of
kern/bootstrap.c plus a parallel walker, machine_exec_boot_script(),
that read /chosen/multiboot,module nodes out of the DTB and fed them
into boot_script_parse_line() directly.  That rewrite forked
intr.c/intr.h and bootstrap.c off from the i386 branch and made the
port aarch64-only.

Use a smaller wedge: leave kern/bootstrap.c untouched and have
aarch64's c_boot_entry() synthesise the same multiboot-shaped
boot_info that bootstrap_create() already knows how to read.

Concretely:

  * aarch64/include/mach/aarch64/multiboot.h declares the minimal
    subset of the multiboot1 layout that bootstrap.c references —
    struct multiboot_module, struct multiboot_raw_info and the
    MULTIBOOT_MODS flag — with pointer-sized fields appropriate
    for a kernel-synthesised table that never crosses the
    loader/kernel boundary.

  * aarch64/aarch64/model_dep.c defines `struct multiboot_raw_info
    boot_info`, matching i386/i386at/model_dep.c.  The old
    machine_exec_boot_script() is replaced by
    load_boot_modules_from_dtb(), which translates each DTB
    multiboot,module node into one entry of a static
    boot_modules[BOOTSTRAP_MAX_MODULES] table and points
    boot_info.mods_addr at it.

    load_boot_modules_from_dtb is marked noinline on purpose:
    pmap_bootstrap() switches sp from the physical boot stack
    into the high virtual mapping mid-c_boot_entry, so any locals
    whose addresses were hoisted into callee-saved registers
    before the switch end up pointing into the low-half VA range
    (which then traps with SoftPAN).  A separate frame, built
    after pmap_bootstrap() returns, has its stack addresses
    computed against the virtual sp.

  * aarch64/aarch64/irq.{c,h} declare and define `pic_mode = 0`.
    Upstream device/intr.c's irqgetstat() returns it for the
    IRQGETPICMODE flavor; the GIC is the only interrupt
    controller on aarch64, so the value is a constant.

With these in place the unmodified kern/bootstrap.c reads
/chosen/multiboot,module entries via boot_info and runs the same
boot-script-execution path on aarch64 as on i386/x86_64.  Booting
on qemu-system-aarch64 -M virt without modules reaches the
expected `No bootstrap modules loaded with Mach' panic.
---
 aarch64/Makefrag.am                      |   2 +
 aarch64/aarch64/irq.c                    |   8 ++
 aarch64/aarch64/irq.h                    |   2 +
 aarch64/aarch64/model_dep.c              | 124 ++++++++++++++---------
 aarch64/include/mach/aarch64/multiboot.h |  61 +++++++++++
 5 files changed, 149 insertions(+), 48 deletions(-)
 create mode 100644 aarch64/include/mach/aarch64/multiboot.h

diff --git a/aarch64/Makefrag.am b/aarch64/Makefrag.am
index cfcc1f6c..76293156 100644
--- a/aarch64/Makefrag.am
+++ b/aarch64/Makefrag.am
@@ -47,6 +47,7 @@ libkernel_a_SOURCES += \
 	aarch64/aarch64/irq.c \
 	aarch64/aarch64/locore.h \
 	aarch64/aarch64/locore.S \
+	aarch64/aarch64/lock.h \
 	aarch64/aarch64/mach_aarch64.c \
 	aarch64/aarch64/pcb.c \
 	aarch64/aarch64/percpu.c \
@@ -105,6 +106,7 @@ include_mach_aarch64_HEADERS = \
 	aarch64/include/mach/aarch64/mach_aarch64.defs \
 	aarch64/include/mach/aarch64/mach_aarch64_types.h \
 	aarch64/include/mach/aarch64/machine_types.defs \
+	aarch64/include/mach/aarch64/multiboot.h \
 	aarch64/include/mach/aarch64/syscall_sw.h \
 	aarch64/include/mach/aarch64/thread_status.h \
 	aarch64/include/mach/aarch64/vm_param.h \
diff --git a/aarch64/aarch64/irq.c b/aarch64/aarch64/irq.c
index 4e5223b7..0b06684d 100644
--- a/aarch64/aarch64/irq.c
+++ b/aarch64/aarch64/irq.c
@@ -19,3 +19,11 @@
 #include "aarch64/irq.h"
 
 struct irq_src	*root_irq_src;
+
+/*
+ *	device/intr.c exposes IRQGETPICMODE through irqgetstat(), and reads
+ *	pic_mode to report which interrupt-controller mode the kernel is
+ *	using.  On aarch64 there is no PIC/APIC distinction; the GIC is the
+ *	only option, so report a fixed value here.
+ */
+int pic_mode = 0;
diff --git a/aarch64/aarch64/irq.h b/aarch64/aarch64/irq.h
index bfc3472f..6bd19f00 100644
--- a/aarch64/aarch64/irq.h
+++ b/aarch64/aarch64/irq.h
@@ -43,6 +43,8 @@ void __disable_irq (irq_t irq);
 
 extern void unmask_irq (unsigned int irq_nr);
 
+extern int pic_mode;
+
 #endif /* device/intr.h legacy */
 
 
diff --git a/aarch64/aarch64/model_dep.c b/aarch64/aarch64/model_dep.c
index 1105daf8..f67d30a8 100644
--- a/aarch64/aarch64/model_dep.c
+++ b/aarch64/aarch64/model_dep.c
@@ -30,6 +30,7 @@
 #include <kern/startup.h>
 #include <kern/bootstrap.h>
 #include <kern/boot_script.h>
+#include <mach/machine/multiboot.h>
 #include <string.h>
 
 #include <device/intr.h>	/* FIXME */
@@ -54,7 +55,8 @@ typedef struct
 
 const char *kernel_cmdline;
 
-char /*struct start_info*/ boot_info;
+static void load_boot_modules_from_dtb(void);
+
 struct irqdev irqtab;
 int iunit[1];
 interrupt_handler_fn ivect[1];
@@ -333,18 +335,45 @@ void __attribute__((noreturn)) c_boot_entry(dtb_t dtb)
 	machine_slot[0].cpu_type = CPU_TYPE_ARM64;
 	init_percpu(0);
 
+	load_boot_modules_from_dtb();
+
 	setup_main();
 	__builtin_unreachable();
 }
 
-void machine_exec_boot_script(void)
+/*
+ *	On x86 this is filled in by the multiboot1 loader (see
+ *	i386/i386at/model_dep.c).  aarch64 doesn't get one of those, but
+ *	kern/bootstrap.c is structured around walking boot_info.mods_addr,
+ *	so we synthesise an equivalent table from the DTB's
+ *	/chosen/multiboot,module nodes during early boot.
+ */
+struct multiboot_raw_info boot_info;
+
+#define	BOOTSTRAP_MAX_MODULES	10
+static struct multiboot_module boot_modules[BOOTSTRAP_MAX_MODULES];
+
+/*
+ *	Walk /chosen/multiboot,module nodes in the DTB and translate each
+ *	into a multiboot_module entry that kern/bootstrap.c can consume
+ *	verbatim.  QEMU's -device guest-loader synthesises exactly these
+ *	nodes, so any module passed via guest-loader becomes available
+ *	through the standard mods_addr/mods_count interface.
+ *
+ *	noinline keeps this function's locals out of c_boot_entry's frame.
+ *	c_boot_entry's stack-local addresses are computed pre-MMU (sp still
+ *	physical), and pmap_bootstrap() switches sp into the high virtual
+ *	mapping mid-function.  An inlined version would have stack pointers
+ *	hoisted into callee-saved registers before the switch, leaving us
+ *	writing to physical addresses after the MMU is enabled.  A separate
+ *	frame avoids that by being built with sp already virtual.
+ */
+static __attribute__((noinline)) void load_boot_modules_from_dtb(void)
 {
-	struct dtb_node 	chosen, node;
-	struct dtb_prop 	prop;
+	struct dtb_node		chosen, node;
+	struct dtb_prop		prop;
 	unsigned short		address_cells, size_cells;
-	struct bootstrap_module	bmods[10];
-	int			i = 0, err, losers = 0;
-	const char		*args;
+	int			i = 0;
 	vm_offset_t		off;
 
 	chosen = dtb_node_by_path("/chosen");
@@ -352,51 +381,50 @@ void machine_exec_boot_script(void)
 		panic("No chosen node in DTB\n");
 
 	dtb_for_each_child (chosen, node) {
-		if (dtb_node_is_compatible(&node, "multiboot,module")) {
-			assert(i < 10);	/* 10 boot modules ought to be enough for anybody */
-			prop = dtb_node_find_prop(&node, "bootargs");
-			if (DTB_IS_SENTINEL(prop))
-				panic("No bootargs for bootstrap module %d %s\n", i, node.name);
-			args = (const char *) prop.data;
-			printf("module %d: %s\n", i, args);
-
-			prop = dtb_node_find_prop(&node, "reg");
-			assert(!DTB_IS_SENTINEL(prop));
-			address_cells = node.address_cells;
-			size_cells = node.size_cells;
-
-			/*
-			 *	Work around an apparent QEMU guest-laoder bug,
-			 *	where it unconditionally uses address/size cell
-			 *	size of 2, yet doesn't set (or respect previously
-			 *	set) #address-cells / #size-cells properties in
-			 *	the parent node.
-			 */
-			if (prop.length == 16 && address_cells == 2 && size_cells == 1)
-				size_cells = 2;
-
-			off = 0;
-			bmods[i].mod_start = dtb_prop_read_cells(&prop, address_cells, &off);
-			bmods[i].mod_end = bmods[i].mod_start + dtb_prop_read_cells(&prop, size_cells, &off);
-
-			/* FIXME: we probably should make a copy of this string */
-			err = boot_script_parse_line(&bmods[i], args);
-			if (err) {
-				printf("Error: %s\n", boot_script_error_string(err));
-				losers++;
-			}
-			i++;
-		}
+		if (!dtb_node_is_compatible(&node, "multiboot,module"))
+			continue;
+		if (i >= BOOTSTRAP_MAX_MODULES)
+			panic("Too many bootstrap modules (max %d)\n",
+			      BOOTSTRAP_MAX_MODULES);
+
+		prop = dtb_node_find_prop(&node, "bootargs");
+		if (DTB_IS_SENTINEL(prop))
+			panic("No bootargs for bootstrap module %d %s\n",
+			      i, node.name);
+		printf("module %d: %s\n", i, (const char *) prop.data);
+		boot_modules[i].string = (vm_offset_t) prop.data;
+
+		prop = dtb_node_find_prop(&node, "reg");
+		assert(!DTB_IS_SENTINEL(prop));
+		address_cells = node.address_cells;
+		size_cells = node.size_cells;
+
+		/*
+		 *	Work around an apparent QEMU guest-loader bug,
+		 *	where it unconditionally uses address/size cell
+		 *	size of 2, yet doesn't set (or respect previously
+		 *	set) #address-cells / #size-cells properties in
+		 *	the parent node.
+		 */
+		if (prop.length == 16 && address_cells == 2 && size_cells == 1)
+			size_cells = 2;
+
+		off = 0;
+		boot_modules[i].mod_start =
+			dtb_prop_read_cells(&prop, address_cells, &off);
+		boot_modules[i].mod_end = boot_modules[i].mod_start
+			+ dtb_prop_read_cells(&prop, size_cells, &off);
+		boot_modules[i].reserved = 0;
+		i++;
 	}
+
 	if (i == 0)
 		panic("No bootstrap modules loaded with Mach\n");
-	if (losers)
-		panic("Failed to parse boot script\n");
+
+	boot_info.mods_count = i;
+	boot_info.mods_addr = (vm_offset_t) boot_modules;
+	boot_info.flags |= MULTIBOOT_MODS;
 	printf("%d bootstrap modules\n", i);
-	err = boot_script_exec();
-	if (err)
-		panic("Failed to execute boot script: %s\n", boot_script_error_string(err));
-	/* TODO free memory */
 }
 
 vm_offset_t timemmap(dev_t dev, vm_offset_t off, vm_prot_t prot)
diff --git a/aarch64/include/mach/aarch64/multiboot.h b/aarch64/include/mach/aarch64/multiboot.h
new file mode 100644
index 00000000..a1795043
--- /dev/null
+++ b/aarch64/include/mach/aarch64/multiboot.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2026 Free Software Foundation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _MACH_AARCH64_MULTIBOOT_H_
+#define _MACH_AARCH64_MULTIBOOT_H_
+
+/*
+ *	aarch64 boots via the Linux arm64 boot protocol, not multiboot,
+ *	but kern/bootstrap.c is structured around multiboot's view of
+ *	"the kernel was handed a list of boot modules".  The aarch64
+ *	boot path synthesises a multiboot-shaped module list from the
+ *	DTB's /chosen/multiboot,module nodes during c_boot_entry, so
+ *	the same bootstrap_create() works unchanged.
+ *
+ *	This header therefore exposes only the subset of the multiboot1
+ *	layout that bootstrap.c actually touches: struct multiboot_module,
+ *	struct multiboot_raw_info, and the MULTIBOOT_MODS flag.  Fields
+ *	are sized as vm_offset_t (64-bit on aarch64) rather than the 32-bit
+ *	fields of the on-the-wire protocol, since this struct is populated
+ *	in-kernel and never crosses the kernel/loader boundary.
+ */
+
+#define MULTIBOOT_MODS		0x00000008
+
+#ifndef __ASSEMBLER__
+
+#include <mach/machine/vm_types.h>
+
+struct multiboot_module
+{
+	vm_offset_t	mod_start;
+	vm_offset_t	mod_end;
+	vm_offset_t	string;
+	unsigned	reserved;
+};
+
+struct multiboot_raw_info
+{
+	uint32_t	flags;
+	uint32_t	mods_count;
+	vm_offset_t	mods_addr;
+};
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* _MACH_AARCH64_MULTIBOOT_H_ */
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.