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