Start with making your "flashable" image. I use 2GB and work from there. fallocate
can work too, but some filesystems may not support it.
dd if=/dev/zero of=disk.img bs=1M count=2048
Create your partitions on the disk. I use fdisk and generate a gpt partition table. Note that I leave a slight offset (16MB) for U-Boot and the Rockchip loader stuff, but in my case I will be using U-Boot from the internal SPI and the storage space is entirely optional.
fdisk disk.img
I create my first partition at offset 32768 (16MB exactly) and have it end at 294911 (128MB + 16MB). This gives my boot partition 128MB to work with and ensures that my root partition starts at sector 294912. When working with 512 byte blocks (which we almost always are) it's important to ensure your starting block numbers are always divisible by 8 so that they are 4k aligned.
I change my boot partition to partition type of "Linux extended boot" (BC13C2FF-59E6-4262-A352-B275FD6F7172) and my root partition to type "Linux root (ARM-64)" (B921B045-1DF0-41C3-AF44-4C6F280D3FAE). This isn't required, it's just one of those things I do. I also name my partitions to "boot" and "root".
Once my partitions are created, I mount the disk image using kpartx and begin formatting them.
sudo kpartx -av disk.img
I use fat32 for my boot partition. EXT4 and even BTRFS could work depending upon your U-Boot settings though. Please note unless you change your partition type for the boot partition to "Microsoft Basic" it won't be readable on a Windows machine. I use the tried and true EXT4 for my root partition. If you want to use something like "flash-kernel" to manage your device, you must use EXT4 for your boot filesystem too.
sudo mkfs.vfat /dev/mapper/loop1p1
sudo mkfs.ext4 /dev/mapper/loop1p2
Now we start to create our root filesystem. First, create a mountpoint. I use /tmp/tmpdir.
sudo mkdir /tmp/tmpdir
Then, mount your root filesystem.
sudo mount /dev/mapper/loop1p2 /tmp/tmpdir
Create a boot directory, then mount it too.
sudo mkdir /tmp/tmpdir/boot
sudo mount /dev/mapper/loop1p1 /tmp/tmpdir/boot
Now start installing Debian into your disk image with debootstrap. Since I assume we're doing this on an x86 device, make sure qemu-user-static and binfmt-misc are installed so we can chroot into it just like it's an x86 root.
sudo debootstrap --arch=arm64 --foreign bookworm /tmp/tmpdir
Once that step is complete, we'll need to finish the bootstrapping of the system.
sudo chroot /tmp/tmpdir /bin/bash
Now from within that chroot (remember, we're essentially emulating ARM at this point) you'll finish the install by running the following. All commands at this point are run as root, so sudo isn't necessary or even installed at first.
debootstrap/debootstrap --second-stage
Once done, start by adding your users and setting up a few more things.
adduser username
Install sudo.
apt-get install sudo
Add your user to sudo, or else they can't do anything.
usermod -aG sudo username
Update your timezone.
dpkg-reconfigure tzdata
Install and then configure locales.
apt-get install locales
dpkg-reconfigure locales
After this you'll still need to create and edit your necessary boot.scr, load your kernel, modify your /etc/fstab, and edit your /etc/hostname. But, you should have a mostly working system at this point otherwise.
Now, exit your chroot, unmount your image, then write it to your SD card.
exit
sudo umount /tmp/tmpdir/boot
sudo umount /tmp/tmpdir
Assuming your image is mounted as loop0, unmount it with the following.
sudo kpartx -dv /dev/loop0
Now you have a disk image you can write to SD card with dd
. If you want to write a bootloader to it first, you can (but remember to use the conv=notrunc or you'll nuke everything!).
dd if=u-boot-rockchip.bin of=disk.img bs=512 seek=64 conv=notrunc