Fixing eMMC and flashing a custom 3rd party OS from Linux (DietPi, no UART)

Hello,

I recently got RK3328 and after a series of battles with it I wanted to write a success story on how to flash custom Arm64 OS such as DietPi onto libre board, especially one of the older ones that don’t have a more advanced bootloader such as U-boot on them as well as erasing/fixing eMMC without the use of UART cables / GPIO pins or additional boards, all from within the Linux environment. It will require you to be comfortable unplugging/replugging eMMC module directly into the board while it is powered on, so remove the board from any enclosures beforehand.

Tools we will use:

  • micro SD card

    • we will flash a libre U-boot bootloader via libre-flash-tool and place compressed images of distros (one of libre and a custom one - dietpi) and a single libre bootloader .bin file for your particular board.
    • tested with two 32Gb class 10 micro SD cards by Braveeagle and Sandisk.
  • USB stick

    • we will flash it with uncompressed image (It will become our live bootable USB where we will use Gparted to flash our eMMC)
    • tested with these and even these USB sticks
  • libre eMMC module

    • a pincette/set of tweezers can help to easily remove eMMC module from the board (owning UART cable is ideal, but this is a tutorial for those who don’t have such tools)
  • GParted to easily view your devices and their partitions.

    • :check_mark: For sanity checks remember to hit Refresh Devices (Ctrl+R) to check on your devices partitions stats whenever you perform changes with commands like dd .

  • For flashing use dd command or Balena Etcher (I believe that etcher works with uncompressed .img files, so you will need to uncompress .img.xz first with xz -dk in.img.xz). I personally only used etcher to flash USB with a libre OS image.

    • :skull: Using dd is absolutely fine but be extra careful to type in correct device descriptor (i.e. of=/dev/mydisk) as dd will absolutely wipe your drives. It is known as a Disk Destroyer for a reason.

  • blkid command to list /dev/ devices, device labels/UUIDs, and their block sizes conveniently in a list.

    • :light_bulb:You can mount devices with mount command: mount –uuid=”UUID” /media/your-username/existingFolder or pass in –label=”Label”

  • A keyboard - in order to interrupt the board during U-boot stage (by hitting any key) in order to empty our non-booting eMMC module

  • HDMI cable and a monitor that can pickup board’s signal on boot (not every monitor can or will be able to)

  • An AI tool is of great help to you, in case any of the steps mentioned here are not clear. I used Gemini to help me on this journey, ymmv.

  • A good power supply. As mentioned in many other threads simply plugging board into your computer at 500mAh is simply not enough.

:light_bulb:In case of older boards like RK3328 they don’t come with a more advanced bootloader built into it, namely U-Boot. So the board’s bootloader immediately tries to load an OS from any eMMC (mmcblk0), then from any SD (mmcblk1), and then from any USB devices. If eMMC for some reason is unable to load the OS, your board will simply stop attempting to try and load the next item in this list. Even if you manage to get to any U-Boot prompt and attempt to set env boot_targets usb and boot right after - it may simply not work, so keep that in mind.

:inbox_tray: When you plug-in a micro SD card you want it to have a libre board compatible variant of a U-Boot bootloader. Libre is providing a libre flash tool to flash such a bootloader onto your media of choice. Since this is a tutorial for Linux you may find yourself using a LEFT tool on Windows and your mileage may vary.

Libre has a great set of videos on how to flash boards from within Windows on Youtube. There are also many videos online on how to use USB A to USB A cable and flash eMMC directly from any another SBC boards like Raspberry pi, in case you happen to have one.

micro SD card:

This is loaded as mmcblk1 and will be the second in line to load after eMMC by RK3328 board.

Flashing it with libre bootloader:

Insert in into your computer, and as root unmount it from your system, usually via sudo umount /media/your-username/sd-card-parition-label.

Run libre flash tool (lft.sh) to list devices:

lft.sh dev-list should list it (ex. in my case - sdc; in sudo blkid SD paritions would come up as /dev/sdc1 and so on)

List of lft-recognized libre board names:

lft.sh board-list should show your board as ex. roc-rk3328-cc or roc-rk3328-cc-v2

We are ready to flash custom (libre) U-boot bootloader onto SD card:

sudo ./lft.sh bl-flash roc-rk3328-cc sdc

Confirm that you are flashing to a desired SD card with Y selection and continue on with flashing.

:light_bulb:Now you have a micro SD card with a flashed libre U-boot bootloader that your board can recognize. Board will only load it if eMMC is empty or is unplugged from the board. If eMMC is corrupted the board will continue attempt to load from it.

:inbox_tray: Add files 1, 2, 3

Let’s make our micro SD a little bit more useful by placing compressed OS images (.img.xz) on it AND a board bootloader (.bin file, from here - for this board I used :inbox_tray: 1. roc-rk3328-cc-boot.bin).

We will need one libre approved OS image from libre distro ci (keep the file as we will later on flash it uncompressed onto USB). Download the one for your board - ex. :inbox_tray: 2. debian-12-gnome-arm64+roc-rk3328-cc.img.xz. and download one custom Arm image (for this tutorial purpose a DietPi image - locate ROCK64 on dietpi website and :inbox_tray: 3. click to download it).

Now go to your system’s Gparted, select your SD device(ex. sdc) and partition unallocated space (ex. ext4, fat32).

You can mount your newly created partition on your system and place board’s bootloader .bin file (1), libre .img.xz (2) and dietpi’s .img.xz (3) image files on it.

Run sync command to completion to know that all writes have finished.

Unmount your SD via sudo umount /media/your-username/sd-card-label. Run sudo udisksctl power-off -b /dev/sdc`to power off the SD card. Unplug it and insert into the board.

:memo: Note: Since this tutorial deals with previously flashed EMMCs and no extra SBC board we will NOT need to add a flash.ini file which points to an image you want to flash onto eMMC. (ex. IMAGE_FILE=debian-12-gnome-arm64+roc-rk3328-cc.img.xz). Also you won’t have any success substituting IMAGE_FILE= directly for a third-party OS in here, such as dietpi image, as we will have no way of flashing a required custom libre bootloader on top without UART/or connecting USB A eMMC flashing cable. Without it your eMMC will not boot as dietpi’s bootloader is not going to be enough to boot the OS up. If you already have your flash.ini and this is not a fresh EMMC module you can keep this file in as libre flash tool should not attempt to re-flash on a previously used module from my experience.

USB:

Similarly plug it into your computer, run sudo blkid to get it’s name and note a BLOCK_SIZE. Unmount the USB if it was automatically mounted by your system. Next, flash .img.xz image via sudo xzcat /path/to/your/roc-some-rk3328-cc.img.xz | sudo dd of=/dev/your-usb bs=1M status=progress where bs= corresponds to a BLOCK_SIZE like 512,1M,4M,etc.

:warning: We are flashing USB with libre-approved OS, and NOT with a third-party OS like dietpi. This guarantees that we will be able to boot/use it as a live USB.

Run sync afterwards just in case.

:check_mark: Perform a sanity check that you actually see a Linux filesystem on a USB - mounting it up should show you a /boot path, etc. (may again have to remount it somewhere under /media/your-username/usb-parition). Dietpi will mount 2 paritions, one of which will be called DIETPISETUP.

Similarly power-off your usb via udiskctl power-off -b /dev/usb-name, unplug it, and insert it back into the board - for RK3328 it has to be the top slot of a double usb 1.0 block - this is a slot that happens to be furthest away from the board).

EMMC:

:warning: if this is a new and unflashed eMMC module, your flash.ini will end up writing an OS image onto it. This is not something we want to happen as we need a libre board-specific bootloader to be on the eMMC board.

“Make sure” your eMMC is corrupted or is empty (i.e board’s both green and red lights are on / libre U-boot does not load for you when SD is plugged in)

  • Unplug your eMMC for now (use tweezers and gently pull it up from the board in place where it is attached to the board)

  • Keep both SD and USB plugged in (USB should be in the correct spot as mentioned above), plug in keyboard and HDMI cable, turn your monitor on

  • Boot up your board

  • :placard: At this point you should immediately see a green light disappearing (depends on your board, we are looking at any indication that board is loading from SD card (mmcblk1)). You should see libre U-boot loading up visually.

    :skull: If this doesn’t happen - this is where you have to check if power is adequate, SD card is adequate or plug in UART cable to see what is actually going on.

  • Hit Esc so that U-boot stops attempts to load from SD card and gives you the prompt.

  • Now reconnect your eMMC module back to the board, take your time attaching it back.

  • In U-boot prompt type mmc reset. U-boot should pick up newly attached eMMC. Type mmc list

  • You should see:

mmc@ff500000: 1 (SD)
mmc@ff520000: 0 (eMMC)
  • (SD) label may be missing, don’t worry about it. Select eMMC by typing mmc dev 0
  • Type mmc info and notice following
Rd Block Len: 512
Capacity: 14.6 GiB
Boot area 0 is not write protected
Boot area 1 is not write protected
  • This is enough for us to proceed to erasing contents of eMMC:
    Type mmc erase 0 <X> where <X> is a size in blocks that matches to your eMMC capacity measured in 512 Byte blocks. Since I have a 16GiB eMMC (says 14.6 GiB), you may not need to delete it all, most likely first three gigabytes should be fine. But anyways run mmc erase 0 30618420 to erase whole thing, it takes around 5 minutes.

    • 🖩14.6 GiB * (1024 MiB/1 GiB) * (1024 KiB/1 MiB) ​* (1024 B/1KiB) = 14.6 * 1024 * 1024 * 1024 ≈ 15,676,630,630.4 Bytes

      To find the number of 512byte blocks, divide the total bytes by it: 15,676,630,630.4 B / (512 B/block) ≈ 30,618,420

      :person_shrugging: idk but when I asked AI it gave me 30616290 for some reason

      alternatively in Linux/more advanced U-boot something like emmc dd if=/dev/zero of=/dev/emmc should work

  • :+1: Great! Now even if you reboot the board it will not find anything on eMMC and be able to continue booting from the next item on the list, namely micro SD card (mmcblk1). Since we have no OS installed on it (or flash.ini in case of a new eMMC module) board will simply skip to loading from USB.

First boot from live USB

:warning: If you already have succeeded in booting from libre USB in the past, simply move to the next section. For me u-Boot had to be told manually to load from the USB one time via run bootcmd_usbmentioned below.

:memo: following never worked for me I think, but I will leave it here just in case: you “can” control board boot order and change it in U-boot via env set boot_targets usb mmc1 mmc0 or setenv boot_targets usb mmc1 mmc0 ; env print boot_targets - visually confirm changed order; env save;. Or directly from Linux via storing on eMMC’s /boot/efi/boot.inifile (as root) a line: boot_targets=usb mmc1 mmc0. Again, I don’t think this ever worked in my case on RK3328, so let’s move along.

Let’s perform our first boot from our USB

  • In U-boot type run bootcmd_usb or run boot_targets=usb or boot after setting boot_targets depending on your board and it should start loading live USB.

:raising_hands: If USB boots up we are pretty much ready to flash our eMMC with a custom dietpi image that we have downloaded earlier onto our SD card, as well as flash it with a board-specific bootloader.

Run sudo apt update and apt install gparted if you haven’t yet done so on live USB OS.

:check_mark: Perform a sanity check by opening up GParted. Switch to /dev/mmcblk0 device. It should list all of eMMC contents as one big unallocated space.

Mount the micro SD card partition that contains .img.xz and .bin files somewhere onto /media/your-username/sdcard.

Run sudo xzcat /media/your-username/sdcard/dietPi.img.xz | sudo dd of=/dev/mmcblk0 bs=512 status=progress

Once it’s done refresh the devices in GParted and you should see eMMC partitions - drive will appear split into 4 sections:

  1. Unallocated space 16MiB
  2. /dev/mmcblk0p1 1.17GiB (dietpi root OS partition)
  3. /dev/mmcblk0p2 1.00MiB (labelled DIETPISETUP)
  4. Unallocated space (remainder of your eMMC space, can be allocated)

:memo: DietPi have conveniently skipped 16MiB space. This contains a partition table and that is where RK3328 board is expecting to see a bootloader (exactly at 32KiB mark). The boot process is a multi-stage sequence that is not dependent on the order of partitions.

Let us add that .bin bootloader file in so that the board will be able to load custom OS right from the eMMC.

Run following command to write our bootloader at the exact spot (and not truncate disk afterwards):

sudo dd if=/media/your-username/sdcard/roc-rk3328-cc-boot.bin of=/dev/mmcblk0 bs=512 seek=64 conv=fsync,notrunc

:thinking: What is seek=64? I thought we need to skip 32KiB

This is exactly what this command is doing!

Remember that the blocks on eMMC are 512 bytes in size, and conveniently (512 bytes * 64 blocks = 32,768 bytes). We tell dd to skip 32KiB from the start of out output file (of) and write contents of our input file (if) there.

So in fact we are placing libre board-compatible bootloader right where the board will look for it, after disk’s partition table.

:check_mark: In Gparted Refresh devices / hit Ctrl+R and make sure you still see first three parts of eMMC device intact (unallocated, os, dietpisetup, …). If you forget to add seek to the command above you will end up wiping partition table. If you forget to add notrunc you will truncate the remainder. If that happens you will have to either erase eMMC again from within U-boot or rather simply redo the xzcat and dd and copy dietpi image from within the live USB OS.

Optional: I think you should be able to allocate 4th partition at this point to say ext4 while board haven’t booted up yet from eMMC. Just ran df and apparently dietpi will allocate this space so this may not be needed.

Now we need to modify our DIETPISETUP script, namely dietpiEnv.txt (In case you are installing Armbian it should be named armbianEnv.txt).

Mount /dev/mmcblk0p2 within live USB onto /media/your-username/dietpi or similar and edit it as root /media/your-username/dietpi/dietpiEnv.txt - add a line fdtfile=rockchip/rk3328-roc-cc.dtb.

:memo: This tells dietpi to load the correct device tree blob for your board. This path is relative from /boot/dtb and rockchip is a symlink to another folder that contains all sorts of the dtbs for different rockchip boards. It will end up merged onto /boot/dtb/rockchip once dietpi installs the OS. You can technically compare dietpi’s rk3328-roc-cc.dtb to libre’s roc-rk3328-cc.dtb via cmpcommand and will see that files are literally different by a single byte differ: byte 7, line 1

Optional: You can also edit dietpi.txt within same folder as described here. Or wait for a second boot and dietpi config and software setup.

Unmount /media/your-username/dietpi/ and power off the board.

Optional: You can unplug USB and SD at this point and/or after reboot in U-boot change boot order back to mmcblk0, depending on your board and needs.

That’s it! Enjoy your dietpi SBC! :flexed_biceps:

P.S. Dietpi takes a while to boot, and comes without any video drivers, so you will need to plug-in ethernet cable to your board and ssh into it once it obtains an IP address from your router. On your monitor you will see U-boot going into dietpi and then what looks like a kernel panic - this is fine as there are no video drivers installed yet.

:mantelpiece_clock: After initial dietpi setup via ssh a subsequent boot will take 5 or more minutes give or take, so wait patiently until attempting to ssh into it.

1 Like