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 working point (retracted as a ceiling 2026-09-15) | 16 MB | Nothing observed: the "mis-mapped SCSI drive" failure has no first-party sighting; 32 MB (stock), 64 MB (bench golden, daily) and 128 MB (040/060 kernels, metal) all run | 🟡 history |
| Total describable RAM | ≈64 MB on a stock 68030 kernel (a 4 KB-page 68060 kernel reaches ≈128 MB) | 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" is retracted (2026-09-15): no first-party observation of its failure mode exists; 32 MB boots on a stock kernel, the bench golden runs at 64 MB every day, the project owner reports the stock 030 line at ≥ 80 MB, and the 040/060 kernels run at 128 MB on real silicon ✅. 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 a 4 KB-page 68060 kernel, and it moves only if the arena moves. ✅
- 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 on a stock kernel nothing between 32 MB and 512 MB has ever booted.
- 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 2026-08 RAM campaign 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 68060 | 4096 B | the same frame count | ≈128 MB | ✅ |
The 060 does not get a bigger arena — it gets 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.
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. The campaign 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 ✅. This
is the concrete case behind the neighbour-sweep method law.
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 | Boots. The largest value with positive evidence on a stock kernel | ✅ |
| 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; the unattended installs and the daily dev box run here |
✅ |
| ≥ 80 MB (stock 030 line) | Reported working by the project owner 2026-09-15; not measured here | 🟡 |
| ≈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 | ✅ |
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 — this cost one project a weeks-long "early hang" investigation attributed to a SCSI driver.
- 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–64 MB on the stock 030 line (the bench golden runs 64); 128 MB on the 040/060 line | 16 MB is only the historical working point; 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 | The only oversized value with positive boot evidence on a stock kernel — 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 hard-wires a single 16 MB window at compile time and maps a dummy bank above it so nothing can coalesce past the ceiling; 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; it does not
replace the 16 MB rule, and it is not the explanation for the 16 MB rule's own failure mode
(mis-mapping the SCSI drive — a separate, older, separately-verified claim ✅).
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 2026-08
RAM campaign, 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. Its method laws
are recorded on quirks and gotchas.
See also¶
- Hardware and requirements — the 16 MB Fast RAM 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¶
sources/research-brief.md§2 (hardware envelope: 4–16 MB Fast RAM) and §4 (kernel HAT/MMU, RAM ceiling) — grounding for the 16 MB configuration rule.- The amix-kerntools root-cause investigation, 2026-07-26 (
docs/kernel-52550-hang.md, commit7cdcdc9): 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 amiberry guest-RAM sizing note (
docs/z3660-amix-guest-ram.md): the operational rule (32 MB is the only oversized value with positive boot evidence; the same cap applies to every knob that hands the guest memory, and a Zorro III fastmem board reaches the ceiling faster because it is AutoConfig RAM) ✅. - 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 Z3660 Amix-interop firmware fork — the compile-time-fixed single 16 MB window, the dummy
bank preventing coalescing past the ceiling, and the
amix_mode-forces-CPU-RAM-off contract ✅. - The 2026-08-12/14 RAM campaign (amix-kerntools with Z3660 and amix-z3660net, workspace record):
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-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. - The 2026-08-14/15 golden-regeneration event (workspace record) ✅ — the live-on-metal
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 same campaign's
z3660eth>80 MB investigation — 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 ✅.