The Amix RAM Ceiling¶
Give an Amix guest too much memory and it does not complain — it dies silently before the console
exists. ✅ The kernel's page_init() allocates one ~60-byte page record per physical page frame
out of the kernel virtual map, never checks the allocation for NULL, and on failure walks that
60-byte stride from physical address 0 instead: straight across chip RAM, Zorro II I/O space,
the CIAs and the custom chipset. There is no panic, no Guru, and no console output at all ✅.
Two limits live on this page, and they are not the same limit:
| Limit | Value | What goes wrong past it | Tag |
|---|---|---|---|
| ~~Fast RAM configuration ceiling~~ 16 MB: a layout requirement of the stock kernel, not a RAM ceiling (corrected 2026-09-29) | 16 MB A3000 motherboard bank | Stock kernel only: a bigger bank loads the kernel below 0x07000000, the A3000 SCSI guess skips, no card 0, root not mounted (VOP_OPEN error 6) — quirks §57. Probing kernels run 64 MB; 040/060 kernels 128 MB (metal) |
✅ |
| Total describable RAM | ≈64 MB on a stock 68030 kernel (2 KiB pages); ≈128 MB on the 4 KiB-page 68040/68060 kernels (measured on a 68060; the 68040 runs the same kernel, not yet measured) | page_init() wild-writes from address 0 — total silence |
✅ |
Present the RAM your kernel can describe (below) and the second limit cannot bite you; the first was folklore. The rest of this page is for when something has already handed the guest more memory than you meant to — a debug/DDR window, a Zorro III RAM board, an accelerator's onboard memory — and you are staring at a machine that produces nothing.
The short version¶
- The "16 MB ceiling" had the wrong mechanism (corrected 2026-09-29): it is not a RAM limit but the stock kernel's A3000-SCSI guess, which fires only when the loader placed the kernel above
0x07000000— on a motherboard-RAM-only A3000, the 16 MB bank (quirks §57) ✅. Kernels that probe for the controller run 64 MB and the 040/060 kernels 128 MB on real silicon ✅. (The 2026-09-15 retraction cited 32 MB and ≥ 80 MB boots on stock kernels without recording their RAM layout; they are withdrawn as evidence 🔴.) See hardware. - 528 MB is proven fatal, in exactly the way described below. ✅
- The ceiling is a fixed 4 MiB kernel arena, not an arithmetic accident — ≈64 MB on the stock 68030 kernel, ≈128 MB on the 4 KB-page 68040/68060 kernels, and it moves only if the arena moves. ✅ (The 128 MB figure is measured on a 68060; the 68040 has the same limit by the same page size, not yet measured on 68040 hardware 🟡.)
- On a kernel whose arena was moved, 88 MB boots durably and networks on real hardware, 128 MB banners and then hangs, and ≈151 MB is the end stop ✅ — but no boot of a stock kernel above 16 MB has its RAM layout recorded (an earlier 32 MB report is withdrawn as evidence 🔴).
- This is a kernel limit, not an emulator bug. Any route that hands the guest memory reaches the same code — emulator RAM knobs, a Zorro III fastmem board, accelerator DDR. ✅
What actually happens past the ceiling¶
Everything in this section is ✅ — it was read live out of a running emulator (see Provenance).
The kernel sizes a struct page array from the total RAM the boot path reports, at 60 bytes per
page frame, and asks the kernel virtual map for the space:
528 MB of RAM
-> 252,707 free page frames
-> a 15,162,420-byte page[] array
-> against ~4 MB of kernel virtual map still free
-> rmalloc() returns NULL
-> the result is never checked, at either level
-> page_init(NULL, 252707, ...) runs anyway
page_init() then does what it always does — walk the array setting a bit in each record — except
the array now starts at physical address 0x00000000:
080c0208: orib #-128,%a0@ ; set bit 7 of this "page record"
080c020c: addaw #60,%a0 ; next record, 60 bytes on
080c0210: cmpal 8129e74,%a0 ; ... until epages
080c0216: bcsw 80c0208
The walk runs from 0x00000000 to 0x00E75C34. Along the way it crosses chip RAM, Zorro II I/O
space (0x00b8xxxx), the CIAs and the custom chipset registers — writing to hardware, not memory.
The machine is dead long before console initialisation, which is why the symptom is silence. ✅
The memory sizing was correct throughout: measured maxpfn was 0x50000 (× 2048 = 0x28000000),
the true top of RAM. The kernel knew exactly how much memory it had — it simply could not describe
that much. ✅
Where the ceiling comes from — a fixed 4 MiB kernel arena¶
Correction (2026-08-14). This page previously derived the ceiling as an arithmetic accident — the kernel virtual map happened to have 4 MB free at the moment of failure — and quoted ≈128 MB (computed) with the exact figure tagged 🔴. The RAM-ceiling work (first-party record, 2026-08-12/14) measured the mechanism rather than inferring it, and the 4 MB is not an accident: it is a fixed arena that the kernel's own layout constants define, so the ceiling is a property of the kernel and not of how much virtual space that particular boot had already spent ✅. The stock figure is ≈64 MB, not ≈128 MB. The wild-write mechanism below is unaffected.
The law, measured ✅:
usable RAM = frames ×
NBPP, where the frame count is bounded by a fixed 4 MiB kernel arena spanning[syssegs, kvsegmap)=0x40040000–0x40440000.
Three consumers share that arena, which is why the whole of it is never available to page[]:
page[]— one ~60-byte record per physical page frame; the array whose failed allocation this page is about ✅.sptalloc()— every kernel mapping of a board window, the primitive a Zorro III driver uses to reach its hardware ✅.- u-areas — one per process ✅.
All three are handed out of the same sptmap resource map, so they are in direct competition: a
driver that maps a large board window with sptalloc() is spending exactly the arena the frame array
needs. That is not hypothetical — see the driver that ate the arena below.
The two headline figures follow from the arena and the page size:
| Kernel | Page size (NBPP) |
What the arena describes | Total RAM | Tag |
|---|---|---|---|---|
| Stock 68030 | 2048 B | ~4 MiB ÷ 60 B per frame, less the arena's other tenants | ≈64 MB | ✅ |
| 4 KB-page 68040/68060 | 4096 B | the same frame count | ≈128 MB | ✅ 68060 (measured) / 🟡 68040 (same kernel, not yet measured) |
The 040/060 kernels do not get a bigger arena — they get twice the RAM per frame, so the same
number of 60-byte records covers twice the memory ✅. The 128 MB figure is a page-size result, not a
headroom result, and it is a property of the kernel line, not of the 68060: the 68040 and 68060
MMUs have no 2 KiB page size, so the 040/060 kernels use 4 KiB pages on both CPUs ✅ (the port asserts
NBPP == 4096), and the 68040 has the same ≈128 MB limit 🟡 (no 68040 boot at 128 MB is recorded
yet). The 68030 line is the different one: its 2 KiB pages give ≈64 MB.
The break-even arithmetic this page used to do — one free-list entry of 2048 pages × 2048 bytes, ÷ 60 ≈ 69,900 frames ≈ 136 MB — is that same arena seen from the outside at the moment it ran out. It was the right order of magnitude for the wrong reason, and it did not account for the arena's other tenants, which is where the factor of two went ✅.
Lifting it — and the coupling law that governs any attempt ✅¶
The ceiling is not a check to switch off; it is the arena's size. Raising it means moving the arena and then re-sizing, from the new arena, every constant that was sized from the old one. That work (first-party record, 2026-08-12/14) did exactly that on a stock 030 kernel and booted the result on real hardware.
The load-bearing rule — and the reason a partial job yields a machine that boots and then dies:
seed ≤ page-table extent ≤ arena. The
sptmapseed the kernel starts its resource map with, the bound on the page tables that describe the arena, and the arena itself are three constants that must move together and stay in that order. Move one alone and the machine boots into a silent inconsistency. ✅
And the neighbour that is not sized from the arena at all: segkmap — the map between kvsegmap
and kvsegu — is sized from physmem, so moving kvsegmap upward silently shrank it. The
fixed distance between kvsegmap and kvsegu has to be preserved as part of the same edit ✅.
The individual patch sites — file, offset, before/after instruction — are deliberately not published here: they are edit points inside a proprietary binary kernel, and it is the mechanism and the coupling law that generalise.
What was actually measured ✅¶
On real hardware, on a kernel whose arena had been moved (except where the row says stock):
| Total RAM | Result | Tag |
|---|---|---|
| 32 MB (stock kernel) | Reported to boot, but the RAM layout was not recorded — withdrawn as evidence (see the short version) | 🔴 |
| 88 MB | Boots durably and networks — a working multiuser machine, not just a banner | ✅ |
| 128 MB (arena-moved 030 kernel) | Prints the full SVR4 banner (134215680 bytes of memory), then hangs post-banner |
✅ observed / 🔴 cause open |
| 128 MB (4 KB-page 68060 kernel, real 68LC060, A4000D + Z3660, 2026-09) | Boots, installs, runs multiuser — Total Unix memory 134213632 |
✅ |
| ≈151 MB | End stop — a further page-table extent bound in the root map; nothing beyond it was reached | ✅ |
| 528 MB | Fatal, in exactly the way described above | ✅ |
On the 1.1 module kernels (emulation, 2026-10-26) ✅: the stock bind code puts a 68030 kernel
into the largest non-chip region with no address limit, and a k30 kernel whose bind region exceeds
about 64 MB never starts (a 128 MB bank fails, 64 MB works); the project's new boot loader therefore
caps a k30 kernel at 64 MiB. A kernel of either line bound at or above 0x40000000 (Zorro III
expansion RAM) never starts either: the kernels identity-map only the low 1 GiB and keep syssegs and
the sptalloc arena at virtual 0x40040000–0x40440000, so the stock bind code's lack of an address
limit is a trap on any box with Zorro III RAM, and the new loader binds only below it. The k46 (68040/68060) kernel
passes at 128 MB. What is left of the arena for drivers once the box runs is on loadable kernel
modules → kernel memory.
The 88 MB row is what supersedes the old text. This page used to say "nothing between 32 MB and 512 MB has ever been booted" and that the boundary had never been bisected. On a stock kernel that remains true. On a kernel with the arena moved it is not: 88 MB is a durable, networked machine ✅.
A worked consumer: a driver that ate the arena¶
The z3660eth ethernet driver failed to find its board on any machine with more than 80 MB of
RAM — which looked exactly like a firmware memory-map clash. It was not: the board's banks read
byte-identical at every window and the host DDR was empty ✅. The cause was sptmap
exhaustion — at that much RAM page[] had taken the arena, and the driver's own 65 pages of
sptalloc() mappings had nothing left to allocate from ✅.
The fix was not a smaller mapping but no mapping: when the board's base is below VSECT1 it is
already inside the identity-mapped low 1 GB, so the driver direct-maps it and its arena cost goes
from 65 pages to zero ✅. See the Z3660 ethernet driver.
The reclaim is confirmed on metal, not just in the build. On a running real A4000 + Z3660 both
drivers' direct-map flags read set in the live kernel, so the 98 arena pages the two of them
would otherwise have page-mapped are hardware fact rather than a compile-time intention ✅. Read a
flag like that with care: it is a one-byte object, and adb's int format prints a set byte as
16777216 — see quirk 46.
That is the general shape of the interaction: a driver's mapping budget and the machine's RAM
ceiling are the same budget. A driver that page-maps a window it did not need to map is spending
frames the kernel wanted for page[].
Recognising it¶
The failure has no error message, so recognise it by shape ✅:
- Zero guest output. No SVR4 banner, no memory line, black screen, no serial log. It presents identically to a dead driver, a mis-built kernel, or missing hardware.
- In an emulator log (Amiberry/WinUAE style host logging), a storm of bus-timeout messages at low Zorro II addresses from a single PC pair, with a constant stride of 0x3C (60):
Gary timeout: 00b80038 0 R PC=080c0208
Gary timeout: 00b80038 0 W PC=080c020c
Gary timeout: 00b80074 0 R PC=080c0208 <- +0x3C
Followed by blitter/BEAMCON0/"disk DMA started" noise as the walk reaches the custom chipset.
- The arithmetic check: the first faulting address is an exact multiple of 60 (0x00B80038 ÷ 60
= 200,978), which is the fingerprint that the walk started at address 0 rather than at a real
array. ✅
Rule of thumb: if an Amix guest emits nothing at all on a configuration you just gave more memory to, suspect the memory before the driver, the kernel or the disk. ✅
How much RAM to present¶
In emulation¶
Uniformly ✅ unless tagged.
| Knob | Value | Why |
|---|---|---|
| Chip RAM | 2 MB (4 MB on the A4000-style rigs) | Standard; not counted against the Fast RAM rule |
| Fast / motherboard RAM | 16 MB A3000 motherboard RAM for the stock kernel; up to 64 MB for a 030 kernel that probes for the A3000 SCSI; 128 MB on the 040/060 line | a bigger motherboard bank breaks the stock kernel's root mount (quirks §57); less than 8 MB livelocks under package-install workloads; stay under the describable-RAM ceiling for your kernel |
| A debug/DDR window (e.g. a stand-in for accelerator memory) | 32 MB if you need one at all | In emulation a 32 MB window over 16 MB of motherboard RAM got kernels with the stock arena past page_init() (to vfs_mountroot and /sbin/init, not to a login), where 512 MB died in the wild write. The 88 MB result below needed the kernel arena moved 🟡 |
| Zorro III fastmem board | avoid | It is AutoConfig RAM counted straight from the memory list, so it reaches the ceiling faster than a mapped window 🟡 |
A window that is not an AutoConfig RAM board can still be counted: if it lands immediately above the machine's motherboard RAM, the A3000 Kickstart's motherboard-RAM sizer walks upward across both and publishes them as one contiguous region, which Amix then believes. That is faithful ROM behaviour, not an emulator defect ✅ — the guest simply cannot describe the result.
See the per-emulator config tables in WinUAE, FS-UAE and Amiberry.
On real hardware¶
The same kernel code runs, so the same limit applies to a real memory map ✅. In practice the supported machines cannot reach it with motherboard RAM alone — the exposure is expansion:
- Accelerator memory. The Z3660's Amix-interop firmware mode presents a single window — sized
by the
amix_ramknob (default 16 MB, bounded 128; the investigation-era firmware fixed it at 16 MB at compile time) — and maps a dummy bank above it so nothing can coalesce past it; the same mode disables the accelerator's large CPU-RAM board ✅. That is what a correct accelerator memory map looks like for Amix. See real hardware. - Zorro III RAM boards. AutoConfig RAM is counted directly, and a modern board can be far larger than anything Amix can describe.
Why the folklore says "chip RAM plus 4–16 MB and nothing exotic"¶
Because that envelope is the one that always fits ✅. The period advice was empirical — nobody in
1992 had a memory map large enough to reach the page_init() failure, and everything at or below
16 MB Fast RAM works. The mechanism on this page is what sits underneath the folklore. The "16 MB
rule" itself — "more than 16 MB mis-maps the SCSI drive" — had the mechanism wrong, not the
symptom (corrected 2026-09-29): on the stock kernel a bigger A3000 motherboard bank loads the kernel
below 0x07000000, the A3000-SCSI guess skips and root is lost (quirks §57) ✅.
Kernels that probe for the controller run 64 MB, and the 040/060 kernels 128 MB on metal ✅.
Where "16 MB" came from (dated note, 2026-09-26). The figure is what amigaunix.com and the common FS-UAE/WinUAE configs document as the Fast RAM setting, and this site repeated it as a kernel ceiling with a failure mode nobody had measured. The measured record replaced it: 64 MB on a 030 kernel that probes for the A3000 SCSI, and 128 MB on the 040/060 kernels on real 68LC060 silicon (2026-09-14) ✅.
Dated note, 2026-09-29. 16 MB was also the one A3000 motherboard layout in which the stock kernel's A3000-SCSI guess fires (quirks §57): the period advice was right for the stock kernel, for the wrong stated reason. The 2026-09-26 version of this note also cited "≥ 80 MB on the stock 030 line"; that report did not record the RAM layout or the root controller and is withdrawn as evidence 🔴.
Provenance — how this was measured¶
✅ throughout, by measurement rather than by reading source (no Amix kernel source exists for this code path — see reverse-engineering the kernel):
- The emulator logs each memory bank's host base address, so guest memory is addressable through
/proc/<pid>/memon the host. Guest code at the faulting PC was pulled out that way and disassembled withm68k-cbm-sysv4-objdump -b binary -m m68k:68030. No debugger, no guest-side instrumentation, no emulator patch. - The function was identified by the strings the compiler left beside it:
"page_init"and"vm_page.c", plus the assertion text"pp >= pages && pp < epages". - Live globals at the hang:
pages = 0x00000000(the NULL base),epages = 0x00E75C34(= 60 × 252,707),maxpfn = 0x50000. - Discriminator: a freshly built, leaner kernel containing a completely different driver set reproduces the fault byte-for-byte at the same RAM size — same first address, same 0x3C stride, same instruction pair, same NULL base. The driver mix is irrelevant, as it must be for a fault in VM initialisation.
- Control: the same kernels and the same disk images boot normally — one to
vfs_mountroot, one all the way to/sbin/init, and one to a full multiuserlogin:— once the memory window is bounded. Six boots, one variable at a time.
The arena half of this page (the fixed [syssegs, kvsegmap) extent, the sptmap sharing, the
≈64/≈128 MB figures and the 88/128/≈151 MB rows) comes from a second, later lineage ✅ — the RAM-ceiling
measurements (first-party record, 2026-08-12/14), which located every constant by instruction context in a disassembly diff, never by
offset, and validated each step by booting a real A4000 rather than an emulator.
See also¶
- Hardware and requirements — the Fast RAM envelope (and the retracted 16 MB rule) in its full context, plus the CPU/MMU/FPU and SCSI-ID limits.
- Quirks and gotchas — the ceiling as a one-line checklist item.
- Kernel architecture — the HAT/MMU layer this memory description feeds.
- Emulation fidelity — the other class of silent Amix boot failure, where the emulator is at fault rather than the configuration.
- Reverse-engineering the kernel — the technique used to read this mechanism out of a binary-only kernel.
Sources¶
- The 1.1 module-kernel bind rows (emulation, 2026-10-26) ✅:
amigaux/amixkernel/dlm/CHANGELOG.md, entries "boot2: a k30 kernel gets at most 64 MiB of fast memory; Z3 RAM is never kernel memory" and "boot2 binds the kernel only below 0x40000000"; the kmem figures inkernel/dlm/docs/measurements/2026-10-26-u1.mdand2026-10-26-u4.md. - The 2026-09-29 stock-kernel RAM-layout measurement ✅ — on the emulated bench the same 64 MB A3000 motherboard bank boots a kernel that probes for the A3000 SCSI and stops the pinned stock kernel at
s5mountroot VOP_OPEN error 6(no card 0), symbolised against the stockautocon()load-address guess (quirks §57). sources/research-brief.md§2 (hardware envelope: 4–16 MB Fast RAM) and §4 (kernel HAT/MMU, RAM ceiling) — grounding for the 16 MB figure, now tagged 🟡 and retracted as a ceiling (2026-09-15).- The
page_init()root-cause investigation (first-party record, 2026-07-26): the live/proc/<pid>/memread of a running guest, thepage_init()/rmalloc()disassembly and live globals (pages= 0,epages=0x00E75C34,maxpfn=0x50000), the kernel-virtual-map free entry (2048 pages × 2048 B), the break-even arithmetic, the SCSI-only discriminator kernel reproducing the fault byte-for-byte, and the six-run ablation table ✅. The same runs give the emulator debug-window sizing (a 32 MB window over 16 MB of motherboard RAM passespage_init(), 512 MB dies; a Zorro III fastmem board reaches the ceiling faster because it is AutoConfig RAM) 🟡 as an operating rule. - The amix-packagemanager bench-RAM investigation, 2026-07-22 (Amiberry, fixed kernel
sum -r42265): 16 MB-exactly proven clean (SCSI read / write-readback / double-read plus a byte-exact base-set tree digest) and the 8 MB memory-exhaustion livelock signature ✅. - The 2026-09-15 retraction of the 16 MB figure as a ceiling, with the 040/060 kernels measured at 128 MB on metal ✅.
- The Z3660 Amix-interop firmware fork — the single Amix window (compile-time fixed at 16 MB in
the investigation-era firmware, since sized by
amix_ram), the dummy bank preventing coalescing past it, and theamix_mode-forces-CPU-RAM-off contract ✅. - The RAM-ceiling measurements (first-party record, 2026-08-12/14):
the fixed 4 MiB kernel arena
[syssegs 0x40040000, kvsegmap 0x40440000), the 60 B/framepage[]cost and itssptmapsharing withsptalloc()and u-areas, the ≈64 MB stock-030 / ≈128 MB 4 KB-page 040/060 figures, theseed ≤ page-table extent ≤ arenacoupling law and thesegkmapsized-from-physmem neighbour, and the metal results at 88 MB (durable, networked), 128 MB (full banner then a post-banner hang, cause open) and ≈151 MB (end stop) ✅. The individual patch sites are deliberately held back — see the note under Lifting it. - A 2026-08-14/15 live readback on metal ✅ — the
confirmation that both direct-map flags read set on the running kernel, i.e. the 98 reclaimed
arena pages are measured rather than inferred, read with
adb -kagainst the on-box native-linked kernel. - The
z3660eth>80 MB investigation (first-party record, 2026-08-12/14) — the ">80 MB board not found" failure root-caused tosptmapexhaustion rather than a firmware map clash (banks byte-identical at every window, host DDR empty), and the direct-map fix taking the driver from 65sptmappages to 0 ✅. - The 040/060 page size (2026-10-29):
amigaux/amix-040-060-portbranchk46-module@587c18a—src/modelb_geom_probe.c(a compile-time assertionNBPP == 4096),docs/boot-path-map.md(2 KB pages are impossible on the 040/060 MMU),STATUS.md(one region up to 128 MiB; the 68040 has the same limit, not yet measured) ✅.