# 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):

```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-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

```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: <https://opensource.rock-chips.com/wiki_Boot_option>
- Rockchip standard partition layout: <https://opensource.rock-chips.com/wiki_Partitions>
- U-boot extlinux format: <https://docs.u-boot.org/en/stable/develop/distro.html>
- PINE64 RK3399 boot sequence (same BootROM search pattern): <https://wiki.pine64.org/wiki/RK3399_boot_sequence>
- Gentoo wiki — first partition at sector 32768: <https://wiki.gentoo.org/wiki/User:Brendlefly62/Rockchip_RK3588S_Rock_5c/Build-Install-U-Boot>
- Khadas forum — confirming 32768 offset: <https://forum.khadas.com/t/edge-v-domain-emmc-what-is-the-right-offset-for-creating-a-new-partition/4921>
- eMMC boot partition docs (linux-sunxi): <https://linux-sunxi.org/Bootable_eMMC>
- OPi 3B bring-up: `docs/initial-bringup.md`
- Storage architecture: `docs/storage-architecture.md`