cyberdeck/docs/emmc-multiboot.md

9.2 KiB

eMMC Multi-Boot Setup — Orange Pi 3B

Status: Revised 2026-06-02. Replaces earlier version that used the eMMC hardware boot partition — that approach failed because the SPL cannot find u-boot.itb on a 4MB boot partition (it looks at sector 16384, which is past the end).

This version uses the by-the-book Rockchip user-area boot method.

Boot Architecture

Rockchip Standard Boot Layout (eMMC/SD user data area)

Sector 0:     Protective MBR
Sectors 1-33: GPT header + partition entries
Sectors 34-63: gap (unused)
Sectors 64-683: idbloader.img (620 sectors / 317KB — RKNS magic at sector 0x40)
Sectors 684-16383: free gap (~8MB)
Sectors 16384-19400: u-boot.itb (3017 sectors / 1.5MB)
Sectors 19401-32767: free gap (~6.5MB)
Sector 32768+: User partitions (first partition starts here, at 16MB alignment)

Source: https://opensource.rock-chips.com/wiki_Boot_option

Boot Flow

BootROM → SPI (erased, skip) → microSD (if inserted, skip) → eMMC user area
  → scans sectors 64, 1088, 2112, 3136, 4160 for "RKNS" magic
  → loads idbloader (SPL + DDR init)
  → SPL loads u-boot.itb from sector 16384 of same device
  → u-boot reads extlinux.conf from /boot partition
  → shows boot menu → loads kernel

Key difference from v1: The BootROM scans the user data area of the eMMC (mmcblk0), not the hardware boot partition (mmcblk0boot0). The eMMC's PARTITION_CONFIG register must be set to boot from user area (0x78), not boot0 partition (0x48).

Why the first approach failed

The hardware boot partition (mmcblk0boot0) is only 4MB (8192 sectors). The SPL binary (idbloader) is configured to find u-boot.itb at sector 16384 of the same device. On the boot partition, sector 16384 doesn't exist — it's past the 8192-sector boundary. The BootROM successfully loaded idbloader, but SPL immediately failed when it tried to read u-boot.itb from an out-of-bounds sector.

Partition Layout

mmcblk0 (233GB eMMC — user data area)
└── mmcblk0
    ├── mmcblk0p1   FAT32   1GB     /boot   (starts at sector 32768)
    ├── mmcblk0p2   ext4    85GB    root_opios   (Orange Pi OS Arch)
    ├── mmcblk0p3   ext4    85GB    root_armbian   (Armbian)
    └── mmcblk0p4   ext4    ~84GB   root_nixos   (NixOS)

All data below sector 32768 is reserved for the bootloader chain (idbloader + u-boot.itb). Do not create partitions there.

Step 1: Switch to User-Area Boot and Verify

This step is critical. The eMMC's PARTITION_CONFIG must be set for user-area boot. Run from a booted Orange Pi OS (either SD card or existing eMMC install):

# Switch to user-area boot with BOOT_ACK enabled
sudo mmc bootpart enable 7 1 /dev/mmcblk0

# Verify
sudo mmc extcsd read /dev/mmcblk0 | grep PARTITION_CONFIG

Expected: PARTITION_CONFIG: 0x78 (bits [5:3] = 111 = user area, bit 6 = BOOT_ACK enabled).

This only needs to be done once. The eMMC retains this setting across power cycles.

Step 2: Wipe and Repartition

sudo sgdisk --zap-all /dev/mmcblk0
sudo blkdiscard /dev/mmcblk0    # optional
sudo gdisk /dev/mmcblk0

In gdisk interactive mode — note the explicit start sector for p1:

o   (new GPT)
n   1   32768   +1G    ef00   name=boot
n   2   default  +85G   8300   name=root_opios
n   3   default  +85G   8300   name=root_armbian
n   4   default  default 8300   name=root_nixos
w   (write, confirm 'y')

p1 starts at sector 32768 (16MB boundary), leaving the entire 0-32767 area for the bootloader. p2/3/4 use default start sectors — gdisk automatically calculates them from the preceding partition's end.

Step 3: Create Filesystems

sudo mkfs.vfat -F 32 -n BOOT /dev/mmcblk0p1
sudo mkfs.ext4 -L root_opios /dev/mmcblk0p2
sudo mkfs.ext4 -L root_armbian /dev/mmcblk0p3
sudo mkfs.ext4 -L root_nixos /dev/mmcblk0p4

Order doesn't matter here — the bootloader sectors (64-683 and 16384-19400) are in the gap between GPT and p1 (which starts at sector 32768). mkfs only touches the partition itself, not the gap. But writing the bootloader first and verifying it's there before moving on makes debugging easier.

Step 4: Write Bootloader to User Data Area

First, find the bootloader files. They may already exist on your running system:

find / -name "idbloader.img" -o -name "u-boot.itb" 2>/dev/null

If found (e.g. at /boot/idbloader.img and /boot/u-boot.itb), use them directly. If not, extract from the SD card — the idbloader starts at sector 64 (skip the GPT header) and u-boot.itb at sector 16384:

sudo dd if=/dev/mmcblk1 of=idbloader.img bs=512 skip=64 count=16320
sudo dd if=/dev/mmcblk1 of=u-boot.itb bs=512 skip=16384 count=8192

Write both to the eMMC user data area at Rockchip-standard offsets. Write to /dev/mmcblk0 (the raw eMMC device), not a partition:

sudo dd if=idbloader.img of=/dev/mmcblk0 bs=512 seek=64
sudo dd if=u-boot.itb of=/dev/mmcblk0 bs=512 seek=16384
sudo sync

Verify the write:

sudo hexdump -C /dev/mmcblk0 -n 16 -s $((64*512))

Should show 52 4b 4e 53 ("RKNS") — the Rockchip boot header magic. That confirms the BootROM will find it.

Why skip=64 on the SD card extraction: The SD card has a GPT at sectors 0-33. idbloader starts at sector 64 (0x40). Without skip=64, the extracted image includes GPT garbage at the beginning.

Why seek=64 and seek=16384: These are the Rockchip-standard offsets where the BootROM and SPL expect to find the bootloader stages. They work for both SD card and eMMC user data area.

Step 5: Mount and Clone the Rootfs

sudo mkdir -p /mnt/root_opios
sudo mount /dev/mmcblk0p2 /mnt/root_opios
sudo mkdir -p /mnt/root_opios/boot
sudo mount /dev/mmcblk0p1 /mnt/root_opios/boot

sudo rsync -axHAWX --info=progress2 / /mnt/root_opios/
sudo rsync -aHAWX /boot/ /mnt/root_opios/boot/

The second rsync is needed because /boot is a separate filesystem on the SD card — -x (first rsync) deliberately avoids crossing filesystem boundaries.

Step 6: Update fstab

sudo blkid /dev/mmcblk0p1 /dev/mmcblk0p2

Edit /mnt/root_opios/etc/fstab. Replace old SD card references with the new eMMC partition UUIDs:

UUID=<boot-UUID>  /boot  vfat  defaults,noatime  0  2
UUID=<root_opios-UUID> /     ext4  defaults,noatime  0  1

Remove any old SD card entries (typically referencing /dev/mmcblk1 or the SD card's UUID).

Step 7: Write extlinux.conf

uname -r
find /mnt/root_opios/boot -name "*.dtb" | grep orangepi-3b

Rather than writing from scratch, copy and modify the SD card's working config:

sudo cp /boot/extlinux/extlinux.conf /mnt/root_opios/boot/extlinux/extlinux.conf
sudo nano /mnt/root_opios/boot/extlinux/extlinux.conf

Replace the UUID in the APPEND root=UUID=... line with root_opios's UUID. Keep everything else (DTB path, console args) as-is — these are known to work with this hardware.

Step 8: Test

sudo umount /mnt/root_opios/boot
sudo umount /mnt/root_opios
sudo sync
sudo poweroff

Remove SD card. Power on. Expected behavior:

  1. Orange Pi logo appears (u-boot running)
  2. Boot menu shows (from extlinux.conf)
  3. Orange Pi OS boots from eMMC

If it doesn't boot, see troubleshooting below.

Troubleshooting

Symptom Likely cause Fix
u-boot logo, then blank screen EDID/HDMI handshake Add video=HDMI-A-1:1920x1080@60e to APPEND line
"No bootable device" or no logo Bootloader not found Verify PARTITION_CONFIG=0x78 (step 1). Verify idbloader was written to /dev/mmcblk0 at seek=64: hexdump -C /dev/mmcblk0 -n 512 -s $((64*512)) should show "RKNS"
"Can't find u-boot.itb" SPL loaded idbloader but can't find u-boot Verify u-boot.itb at seek=16384: hexdump -C /dev/mmcblk0 -n 16 -s $((16384*512)) — should show valid header, not zeros
Kernel panic: can't find root Wrong UUID in extlinux.conf Boot from SD, mount eMMC, fix UUID in /boot/extlinux/extlinux.conf
Nothing on screen at all Power? HDMI connection? Check USB-C power, HDMI cable, try different monitor
Boots only when SD inserted SD card takes priority Boot flow checks SD first. Remove SD card for eMMC-only test

Later: Install Armbian and NixOS

To be documented — Armbian rootfs goes into root_armbian, kernel+DTB added to shared /boot, next LABEL added to extlinux.conf.

NixOS rootfs goes into root_nixos. Likely the trickiest since NixOS generates its own /boot structure.

References