--- /n/sources/plan9/sys/src/9/pc/l.s Tue Jun 16 08:17:17 2026 +++ /sys/src/9/pc/l.s Tue Jun 16 08:17:17 2026 @@ -34,6 +34,8 @@ * mapped, so behave as if the mmu was not setup */ TEXT _startKADDR(SB), $0 + MOVL AX, CX /* save multiboot magic */ + MOVL BX, DX /* save multiboot info addr */ MOVL $_startPADDR(SB), AX ANDL $~KZERO, AX JMP* AX @@ -43,17 +45,8 @@ TEXT _startKADDR(SB), $0 */ TEXT _multibootheader(SB), $0 LONG $0x1BADB002 /* magic */ - LONG $0x00010003 /* flags */ - LONG $-(0x1BADB002 + 0x00010003) /* checksum */ - LONG $_multibootheader-KZERO(SB) /* header_addr */ - LONG $_startKADDR-KZERO(SB) /* load_addr */ - LONG $edata-KZERO(SB) /* load_end_addr */ - LONG $end-KZERO(SB) /* bss_end_addr */ - LONG $_startKADDR-KZERO(SB) /* entry_addr */ - LONG $0 /* mode_type */ - LONG $0 /* width */ - LONG $0 /* height */ - LONG $0 /* depth */ + LONG $0x00000003 /* flags: page align + memory info (no bit 16: the loader uses the ELF program headers, so the kernel is linked with physical load/entry addresses, -P/-E) */ + LONG $-(0x1BADB002 + 0x00000003) /* checksum */ /* * In protected mode with paging turned off and segment registers setup @@ -137,6 +130,10 @@ TEXT m0idtptr(SB), $0 TEXT mode32bit(SB), $0 /* At this point, the GDT setup is done. */ + CMPL CX, $MBOOTREGMAG /* started by a multiboot loader? */ + JNE notmboot + MOVL DX, multibootheader-KZERO(SB) /* save the multiboot info addr */ +notmboot: MOVL $PADDR(CPU0PDB), DI /* clear 4 pages for the tables etc. */ XORL AX, AX MOVL $(4*BY2PG), CX --- /n/sources/plan9/sys/src/9/pc/mem.h Tue Jun 16 08:17:17 2026 +++ /sys/src/9/pc/mem.h Tue Jun 16 08:17:17 2026 @@ -77,6 +77,13 @@ * and that there are 6 of them. */ +/* + * Multiboot magic: MBOOTHDRMAG is in the kernel's multiboot header (l.s), + * MBOOTREGMAG is the value a multiboot loader leaves in AX at entry. + */ +#define MBOOTHDRMAG 0x1badb002 +#define MBOOTREGMAG 0x2badb002 + /* * known x86 segments (in GDT) and their selectors */ --- /n/sources/plan9/sys/src/9/pc/memory.c Tue Jun 16 08:17:17 2026 +++ /sys/src/9/pc/memory.c Tue Jun 16 08:17:17 2026 @@ -12,6 +12,7 @@ #include "fns.h" #include "io.h" #include "ureg.h" +#include "multiboot.h" #define MEMDEBUG 0 @@ -782,6 +783,81 @@ e820scan(void) return 0; } +/* + * Set by l.s (mode32bit) to the loader's multiboot info, or nil if we were not + * started by a multiboot loader. multibootheader must live in the data segment, + * not bss: l.s writes it before _startpg clears bss. + */ +Mbi *multibootheader = nil; +MMap mbmmap[Nmmap]; /* snapshot of the loader memory map */ +int nmbmmap; + +/* + * Snapshot the multiboot memory map while the loader's low memory is still + * intact (called from main() before realmode and screeninit reuse it). + */ +void +multibootinit(void) +{ + int bytes; + Mbi *mbi; + MMap *lmmap; + + nmbmmap = 0; + if(multibootheader == nil) + return; /* not started by a multiboot loader */ + mbi = (Mbi*)KADDR((uintptr)multibootheader); + if(!(mbi->flags & Fmmap) || mbi->mmapaddr == 0) + return; + if(mbi->mmaplength < 2*sizeof(MMap)) + return; /* too few entries to trust */ + lmmap = (MMap*)KADDR(mbi->mmapaddr); + if(lmmap->size == 0 || lmmap->len == 0) + return; /* bogus initial entry */ + bytes = mbi->mmaplength; + if(bytes > sizeof mbmmap) + bytes = sizeof mbmmap; + memmove(mbmmap, lmmap, bytes); + nmbmmap = bytes / sizeof(MMap); +} + +/* + * Feed the multiboot memory map into map(), as e820scan does for the BIOS + * E820 map. Returns -1 if there is no multiboot map. + */ +int +multibootmmap(void) +{ + int i; + ulong base, len; + uvlong last; + MMap *e; + + if(nmbmmap == 0) + return -1; + last = 0; + for(i = 0; i < nmbmmap; i++){ + e = &mbmmap[i]; + if(e->base >= (1LL<<32)) + break; + base = e->base; + if(e->base+e->len > (1LL<<32)) + len = -base; + else + len = e->len; + if(last < e->base) + map(last, e->base-last, MemUPA); + last = e->base+len; + if(e->type == Ememory) + map(base, len, MemRAM); + else + map(base, len, MemReserved); + } + if(last < (1LL<<32)) + map(last, (u32int)-last, MemUPA); + return 0; +} + void meminit(void) { @@ -815,7 +891,13 @@ meminit(void) umbscan(); lowraminit(); - if(e820scan() < 0) + /* + * Prefer the loader's multiboot memory map when present: under a + * multiboot loader (qemu -kernel) the real-mode BIOS E820 probe is not + * available, so e820scan would fault. Fall back to e820scan (BIOS) and + * then ramscan when not multibooted. + */ + if(multibootmmap() < 0 && e820scan() < 0) ramscan(maxmem); /* --- /n/sources/plan9/sys/src/9/pc/mkfile Tue Jun 16 08:17:17 2026 +++ /sys/src/9/pc/mkfile Tue Jun 16 08:17:17 2026 @@ -85,8 +85,14 @@ $LD -o $target -T$KTZERO -l $OBJ $CONF.$O $LIB size $target -# this makes an ELF kernel: -# $LD -o $target.elf -PH5 -R4096 -T$KTZERO -F$KTZEROP -l $OBJ $CONF.$O $LIB +# an ELF kernel, for booting via qemu -kernel (multiboot): qemu's ELF loader +# honours the program headers, placing text/data/bss correctly (the a.out +# multiboot kludge loads contiguously and mis-places the page-aligned data). +# -P/-E set physical load and entry addresses (a multiboot loader enters with +# paging off), as the 9k kernel's mkfile does, so no post-processing is needed. +$p$CONF.elf: $p$CONF + $LD -o $target -H5 -T$KTZERO -P$KTZEROP -E$KTZEROP -R4096 -l $OBJ $CONF.$O $LIB + size $target # don't strip the gzipped kernels -- too frustrating when that's all you have! $p%.gz:D: $p% @@ -101,8 +107,8 @@ strip -o /fd/1 9pccd | gzip -9 >9pccd.gz # we don't need gzipped kernels otherwise, so don't make them -install:V: $p$CONF # $p$CONF.gz - cp $p$CONF /$objtype/ & +install:V: $p$CONF $p$CONF.elf # $p$CONF.gz + cp $p$CONF $p$CONF.elf /$objtype/ & for(i in $EXTRACOPIES) { 9fs $i && cp $p$CONF /n/$i/$objtype && echo -n $i... & } wait @@ -123,6 +129,7 @@ sdiahci.$O: ahci.h devaoe.$O sdaoe.$O: ../port/aoe.h main.$O: init.h reboot.h +main.$O memory.$O: multiboot.h wavelan.$O: wavelan.c ../pc/wavelan.c ../pc/wavelan.h etherwavelan.$O: etherwavelan.c ../pc/wavelan.h devusb.$O usbuhci.$O usbohci.$O usbehci.$O: ../port/usb.h --- /dev/null Tue Jun 16 08:17:17 2026 +++ /sys/src/9/pc/multiboot.h Tue Jun 16 08:17:17 2026 @@ -0,0 +1,66 @@ +/* + * Multiboot information left by a multiboot loader (qemu -kernel, GRUB). + * See https://www.gnu.org/software/grub/manual/multiboot/multiboot.html + */ +typedef struct Mbi Mbi; +struct Mbi { /* multiboot boot information */ + uint flags; + uint memlower; + uint memupper; + uint bootdevice; + uint cmdline; + uint modscount; + uint modsaddr; + uint syms[4]; + uint mmaplength; + uint mmapaddr; + uint driveslength; + uint drivesaddr; + uint configtable; + uint bootloadername; + uint apmtable; + uint vbe[6]; +}; + +enum { /* flags */ + Fmem = 0x00000001, /* mem* valid */ + Fbootdevice = 0x00000002, /* bootdevice valid */ + Fcmdline = 0x00000004, /* cmdline valid */ + Fmods = 0x00000008, /* mod* valid */ + Fsyms = 0x00000010, /* syms[] has a.out info */ + Felf = 0x00000020, /* syms[] has ELF info */ + Fmmap = 0x00000040, /* mmap* valid */ + Fdrives = 0x00000080, /* drives* valid */ + Fconfigtable = 0x00000100, /* configtable* valid */ + Fbootloadername = 0x00000200, /* bootloadername* valid */ + Fapmtable = 0x00000400, /* apmtable* valid */ + Fvbe = 0x00000800, /* vbe[] valid */ +}; + +typedef struct Mod Mod; +struct Mod { + uint modstart; + uint modend; + uint string; + uint reserved; +}; + +/* MMap is also the format of the E820 map, so fixed by BIOS */ +typedef struct MMap MMap; +struct MMap { + uint size; + uvlong base; + uvlong len; + uint type; +}; + +enum { + Nmmap = 32, /* multiboot mmap entries we keep */ +}; + +extern Mbi *multibootheader; /* loader's Mbi addr captured in l.s, nil if not multibooted */ +extern MMap mbmmap[Nmmap]; /* snapshot of the loader's memory map */ +extern int nmbmmap; + +void multibootinit(void); /* snapshot the multiboot tables while low memory is intact */ +int multibootmmap(void); /* feed the snapshot memory map into map(), -1 if none */ --- /n/sources/plan9/sys/src/9/pc/main.c Tue Jun 16 08:17:17 2026 +++ /sys/src/9/pc/main.c Tue Jun 16 08:17:17 2026 @@ -9,6 +9,7 @@ #include "pool.h" #include "reboot.h" #include "mp.h" +#include "multiboot.h" #include Mach *m; @@ -115,6 +116,7 @@ { cgapost(0); mach0init(); + multibootinit(); options(); ioinit(); i8250console();