# 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: ### 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): ```bash # 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 ```bash 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 ```bash 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: ```bash 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: ```bash 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: ```bash 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:** ```bash 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 ```bash 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 ```bash 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 vfat defaults,noatime 0 2 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 ```bash 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: ```bash 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 ```bash 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 - Rockchip boot option: - Rockchip standard partition layout: - U-boot extlinux format: - PINE64 RK3399 boot sequence (same BootROM search pattern): - Gentoo wiki — first partition at sector 32768: - Khadas forum — confirming 32768 offset: - eMMC boot partition docs (linux-sunxi): - OPi 3B bring-up: `docs/initial-bringup.md` - Storage architecture: `docs/storage-architecture.md`