Compare commits
2 commits
5965d20dcb
...
86bc15579d
| Author | SHA1 | Date | |
|---|---|---|---|
| 86bc15579d | |||
| 6347f49fca |
1 changed files with 122 additions and 125 deletions
|
|
@ -1,103 +1,92 @@
|
|||
# eMMC Multi-Boot Setup — Orange Pi 3B
|
||||
|
||||
**Status:** Procedural reference. Last verified: 2026-06-02 (discussion with Jez).
|
||||
**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.
|
||||
|
||||
## Goal
|
||||
## Boot Architecture
|
||||
|
||||
Boot multiple ARM64 OSes from the OPi 3B's onboard 256GB eMMC, with a u-boot boot menu (`extlinux.conf`) appearing at power-on. No SD card needed after setup.
|
||||
|
||||
**Planned OSes:** Orange Pi OS (Arch), Armbian, NixOS
|
||||
|
||||
SPI NOR flash was erased via maskrom and is unused going forward.
|
||||
|
||||
## Boot Chain (after SPI erase)
|
||||
### Rockchip Standard Boot Layout (eMMC/SD user data area)
|
||||
|
||||
```
|
||||
BootROM → SPI (empty, skip) → microSD (if inserted) → eMMC boot partition → extlinux.conf → menu → kernel
|
||||
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)
|
||||
```
|
||||
|
||||
If microSD **is** inserted and has a valid bootloader, it takes priority over eMMC. Useful as a lifeboat/rescue path — if eMMC boot breaks, pop in the SD card and you're back.
|
||||
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
|
||||
|
||||
Proposed new partition schema:
|
||||
```
|
||||
mmcblk1 (256GB eMMC)
|
||||
├── mmcblk1boot0 (hardware boot partition — u-boot SPL + proper)
|
||||
├── mmcblk1boot1 (unused mirror)
|
||||
└── mmcblk1
|
||||
├── mmcblk1p1 FAT32 1GB /boot (extlinux.conf, kernels, DTBs — shared)
|
||||
├── mmcblk1p2 ext4 85GB root_opios (Orange Pi OS Arch)
|
||||
├── mmcblk1p3 ext4 85GB root_armbian (Armbian)
|
||||
└── mmcblk1p4 ext4 ~84GB root_nixos (NixOS)
|
||||
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)
|
||||
```
|
||||
|
||||
### Why a dedicated FAT32 boot partition?
|
||||
All data below sector 32768 is reserved for the bootloader chain (idbloader + u-boot.itb). Do not create partitions there.
|
||||
|
||||
u-boot reads `extlinux.conf` from a FAT, ext4, or btrfs partition. FAT32 is the most universally supported across distro kernel packages. A shared `/boot` means all three OSes drop their kernels and DTBs into one place, and a single `extlinux.conf` controls the menu. 1GB gives headroom for multiple kernel versions across all three OSes.
|
||||
## Step 1: Switch to User-Area Boot and Verify
|
||||
|
||||
## Step 1: Clone SD → eMMC
|
||||
|
||||
Move Orange Pi OS from the microSD card to eMMC and leave two more root paritions ready for other operating systems later.
|
||||
|
||||
### 1.1 — Identify devices
|
||||
|
||||
Run on the running Orange Pi OS (booted from SD):
|
||||
**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
|
||||
lsblk
|
||||
mount | grep mmcblk
|
||||
# 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:
|
||||
- `mmcblk0` = SD card (current boot device)
|
||||
- `mmcblk1` = eMMC (target boot device)
|
||||
Expected: `PARTITION_CONFIG: 0x78` (bits [5:3] = 111 = user area, bit 6 = BOOT_ACK enabled).
|
||||
|
||||
Actual:
|
||||
- `mmcblk0` = eMMC (target boot device)
|
||||
- `mmcblk1` = SD card (current boot device)
|
||||
This only needs to be done **once**. The eMMC retains this setting across power cycles.
|
||||
|
||||
Ground truth state with only OPi OS on SD card:
|
||||
```bash
|
||||
[jez@Rocky ~]$ lsblk
|
||||
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
|
||||
mtdblock0 31:0 0 16M 0 disk
|
||||
mmcblk0 179:0 0 233G 0 disk
|
||||
mmcblk0boot0 179:32 0 4M 1 disk
|
||||
mmcblk0boot1 179:64 0 4M 1 disk
|
||||
mmcblk1 179:96 0 238.4G 0 disk
|
||||
├─mmcblk1p1 179:97 0 213.6M 0 part /boot
|
||||
└─mmcblk1p2 179:98 0 238.2G 0 part /
|
||||
zram0 254:0 0 0B 0 disk
|
||||
```
|
||||
|
||||
### 1.2 — Wipe eMMC partition table
|
||||
|
||||
**Destroys everything on eMMC.** Skip if eMMC is already empty.
|
||||
## Step 2: Wipe and Repartition
|
||||
|
||||
```bash
|
||||
sudo sgdisk --zap-all /dev/mmcblk0
|
||||
sudo blkdiscard /dev/mmcblk0 # optional
|
||||
```
|
||||
|
||||
### 1.3 — Partition eMMC
|
||||
|
||||
```bash
|
||||
sudo blkdiscard /dev/mmcblk0 # optional
|
||||
sudo gdisk /dev/mmcblk0
|
||||
```
|
||||
|
||||
In `gdisk` interactive mode:
|
||||
In `gdisk` interactive mode — **note the explicit start sector for p1:**
|
||||
|
||||
```
|
||||
o (new GPT)
|
||||
n 1 default +1G ef00 name=boot
|
||||
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')
|
||||
```
|
||||
|
||||
### 1.4 — Create filesystems
|
||||
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
|
||||
|
|
@ -106,7 +95,44 @@ sudo mkfs.ext4 -L root_armbian /dev/mmcblk0p3
|
|||
sudo mkfs.ext4 -L root_nixos /dev/mmcblk0p4
|
||||
```
|
||||
|
||||
### 1.5 — Mount and rsync the live rootfs
|
||||
**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
|
||||
|
|
@ -115,103 +141,69 @@ 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/ # /boot is a separate partition, -x skips it
|
||||
sudo rsync -aHAWX /boot/ /mnt/root_opios/boot/
|
||||
```
|
||||
|
||||
Flags: archive, stay-on-filesystem (critical — skips /proc, /sys, /dev), preserve
|
||||
hardlinks/ACLs/xattrs, copy whole files (faster locally). The second rsync handles the
|
||||
separate /boot partition that -x deliberately avoids.
|
||||
The second rsync is needed because `/boot` is a separate filesystem on the SD card — `-x` (first rsync) deliberately avoids crossing filesystem boundaries.
|
||||
|
||||
### 1.6 — Bind mount virtual filesystems
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /mnt/root_opios/{proc,sys,dev,run,tmp}
|
||||
sudo mount --bind /proc /mnt/root_opios/proc
|
||||
sudo mount --bind /sys /mnt/root_opios/sys
|
||||
sudo mount --bind /dev /mnt/root_opios/dev
|
||||
sudo mount --bind /run /mnt/root_opios/run
|
||||
```
|
||||
|
||||
### 1.7 — Update fstab
|
||||
## Step 6: Update fstab
|
||||
|
||||
```bash
|
||||
sudo blkid /dev/mmcblk0p1 /dev/mmcblk0p2
|
||||
```
|
||||
|
||||
Edit `/mnt/root_opios/etc/fstab`. Replace old SD card references with:
|
||||
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
|
||||
```
|
||||
|
||||
### 1.8 — Write extlinux.conf
|
||||
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
|
||||
```
|
||||
|
||||
Create `/mnt/root_opios/boot/extlinux/extlinux.conf`:
|
||||
|
||||
```
|
||||
TIMEOUT 10
|
||||
DEFAULT orangepi-os
|
||||
|
||||
LABEL orangepi-os
|
||||
MENU LABEL Orange Pi OS
|
||||
LINUX /vmlinuz-<kernel-version>
|
||||
INITRD /initrd.img-<kernel-version>
|
||||
FDT /dtbs/rockchip/rk3566-orangepi-3b.dtb
|
||||
APPEND root=UUID=<root_opios-UUID> rootwait console=ttyS2,115200 console=tty1
|
||||
```
|
||||
|
||||
### 1.9 — Install u-boot to eMMC hardware boot partition
|
||||
|
||||
First check if Orange Pi OS ships u-boot binaries:
|
||||
Rather than writing from scratch, copy and modify the SD card's working config:
|
||||
|
||||
```bash
|
||||
find / -name "idbloader.img" -o -name "u-boot.itb" -o -name "*loader*" 2>/dev/null
|
||||
sudo cp /boot/extlinux/extlinux.conf /mnt/root_opios/boot/extlinux/extlinux.conf
|
||||
sudo nano /mnt/root_opios/boot/extlinux/extlinux.conf
|
||||
```
|
||||
|
||||
If files exist at known paths, use them. If not, extract from SD card. The idbloader
|
||||
starts at sector 64 of the SD card (sectors 0-33 hold the GPT), so skip there:
|
||||
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.
|
||||
|
||||
```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 to eMMC hardware boot partition:
|
||||
|
||||
```bash
|
||||
echo 0 | sudo tee /sys/block/mmcblk0/force_ro
|
||||
sudo mmc bootpart enable 1 1 /dev/mmcblk0
|
||||
sudo dd if=idbloader.img of=/dev/mmcblk0boot0 bs=512 seek=64
|
||||
sudo dd if=u-boot.itb of=/dev/mmcblk0boot0 bs=512 seek=16384
|
||||
sudo sync
|
||||
```
|
||||
|
||||
### 1.10 — Clean up and test
|
||||
## Step 8: Test
|
||||
|
||||
```bash
|
||||
sudo umount /mnt/root_opios/boot
|
||||
sudo umount /mnt/root_opios/proc /mnt/root_opios/sys /mnt/root_opios/dev /mnt/root_opios/run
|
||||
sudo umount /mnt/root_opios
|
||||
sudo sync
|
||||
sudo poweroff
|
||||
```
|
||||
|
||||
Remove SD card. Power on. u-boot logo → boot menu → Orange Pi OS from eMMC.
|
||||
Remove SD card. Power on. Expected behavior:
|
||||
|
||||
### Troubleshooting
|
||||
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" | u-boot not written to eMMC boot part | Re-do step 1.9 |
|
||||
| Kernel panic: can't find root | Wrong UUID in extlinux.conf | Boot from SD, mount eMMC, fix UUID |
|
||||
| Nothing on screen at all | SPI flash interference? HDMI? | Re-check SPI erase, try different monitor |
|
||||
| "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 |
|
||||
|
||||
## Step 2: Install Armbian
|
||||
|
||||
|
|
@ -221,9 +213,14 @@ Remove SD card. Power on. u-boot logo → boot menu → Orange Pi OS from eMMC.
|
|||
|
||||
(To be documented — NixOS rootfs goes into root_nixos. Likely the trickiest since NixOS generates its own /boot structure.)
|
||||
|
||||
## Reference
|
||||
## References
|
||||
|
||||
- Rockchip boot flow: <https://opensource.rock-chips.com/wiki_Boot_option>
|
||||
- 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`
|
||||
Loading…
Add table
Add a link
Reference in a new issue