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


Reply via email to