H96 Pro+: getting Armbian onto eMMC and unlocking the missing GB of RAM

· 4 min read

Of all the boxes in this series, the H96 Pro+ was the most cooperative: a genuine 8-core Amlogic S912, 3 GB of physical RAM, and a standalone boot from eMMC with no SD card. Two things still took real effort, though: getting Linux to see all 3 GB, and finishing a root filesystem copy that the memory was blocking.

What is on the board

SoC Amlogic S912, 8 × Cortex-A53 at 1.5 GHz
RAM 3 GB physical
eMMC about 32 GB
USB 2.0 only (480 Mb/s)
Network Gigabit Ethernet
System Armbian (ophub), Debian trixie, kernel 6.18

Every USB port goes through an internal 480 Mb/s hub, so there is no “fast port” to hunt for when you plug in an SSD.

Installing to eMMC from the SD card

The method: boot an Armbian running from an SD card, then run ophub’s installer (armbian-install) and pick the board profile, here ID 203 (H96-Pro-Plus), with an ext4 root filesystem.

Warning. The installer writes to the eMMC. Before you run it, back up the original eMMC (see the “Restore” box below) and make sure the profile you pick matches your board.

First surprise: the root filesystem copy died with an out-of-memory error. The box was copying roughly 1.8 GB while seeing 2 GB of RAM and no swap. Two changes were enough to retry: add swap, and lower vm.dirty_bytes so the kernel flushes earlier instead of piling up dirty pages.

The key point: 2 GB or 3 GB

On first boot, Linux saw only 2 GB. I suspected the device tree first, but it was not the culprit. The difference comes from u-boot:

  • the factory u-boot boots the kernel directly with booti and caps memory at 2 GB;
  • ophub does not load an extra u-boot by default, because the kernel already carries the patch needed for direct boot;
  • a mainline u-boot (the u-boot-zyxq.bin file) reads the true memory size, 3 GB, and hands it to the kernel.

The fix was to chain it: copy u-boot-zyxq.bin to /boot/u-boot.emmc. The emmc_autoscript boot script then loads it with a command of this shape:

fatload mmc 1 u-boot.emmc; go

The full chain becomes: ROM, factory bootloader in the user area of the eMMC, factory u-boot, emmc_autoscript, u-boot.emmc, kernel.

The control test is telling: same device tree, boot without the chained u-boot gives 2 GB, with it 2.8 GiB (3 GB). The device tree plays no part in RAM size on this board.

TODO(nico): attach the free -h output before and after chaining.

Effective memory and swap

With 3 GB you run out quickly. My choice: an 8 GB USB SSD partitioned as GPT, with 4 GB of swap and 3.4 GB of data (Docker volumes, logs). In fstab, the nofail option stops an unplugged SSD from blocking boot.

In front of that swap sits zswap, which compresses pages in RAM (zstd compressor, pool at 33%). It is built into the kernel, so there is nothing to install, and it cuts writes to the eMMC. Kernel parameters live in a sysctl file:

vm.swappiness=100
vm.vfs_cache_pressure=50
vm.dirty_ratio=10
vm.dirty_background_ratio=5
vm.page-cluster=0
vm.min_free_kbytes=32768

On the services side, rpcbind and nfs-blkmap are useless on a headless box and were stopped. chrony, the watchdog (automatic reboot on a hang), unattended-upgrades and vnstat stay on.

Network: DHCP with a stable MAC address

My first instinct was a static IP. That was a mistake, because the box was first set up on a network shared by the PC (Windows connection sharing), on a different subnet from the one behind the switch. A static address picked there breaks as soon as the network changes. The right approach is DHCP with a fixed MAC address (cloned-mac-address=stable in NetworkManager), then a reservation on the router if you need a guaranteed IP.

Restore. Before writing anything to the eMMC, take a complete copy (dd of the whole mmcblk device, plus boot0 and boot1) and keep it off the box. To roll back: boot from the SD card, then write the saved image back to the eMMC.

Takeaways

  • Visible RAM depends on the boot chain, not only on the hardware: check with free -h rather than trusting a spec sheet.
  • A copy that dies on OOM is not an installer failure: tune swap and delayed writes, then retry.
  • SSD swap plus zswap buys headroom without wearing out the eMMC.