Whole process from scratch.
Everything is built from sources.
This guide is covering everything till the moment when we will be able ssh to root to FreeBSD on Orange PI Zero2 (further in the document we may call it OPI).
After the installation the Orange PI Zero2 we will be able to boot from the network without any microSD card installed.
It is not using PXE protocol.
It is using default boot scripts embeeded in the latest u-boot version.
The Orange PI Zero2 hardware loads u-boot application from 2 megabytes SPI-Flash memory, which is installed onboard, and starts u-boot, then u-boot broadcasts DHCP request to the local network looking for the following information:
$ su -
# freebsd-update fetch
# freebsd-update install
# reboot
After PC reboot, again, ssh to build@${{PC_IP}}, enter the following, answering "Y" on all questions.
$ su -
# pkg upgrade
# pkg install bash sudo git gmake bison gcc aarch64-none-elf-gcc python3 py39-pip swig
In the next command: find the string "# %wheel ALL=(ALL:ALL) ALL" and remove "# "
# visudo
Enter the following commands:
# bash
# echo "$(ifconfig|grep broadcast|awk '{print $2}') $(hostname)" >> /etc/hosts
# exit
# exit
$ exit
Great! Everything is ready now to build the FreeBSD from sources for Orange PI Zero2 which has architecture arm64.
$ bash
$ cd
$ mkdir freebsd_13_2
$ cd freebsd_13_2
$ export BASEDIR=$(pwd)
$ export MAKEOBJDIRPREFIX=$BASEDIR/obj
$ git clone https://github.com/freebsd/freebsd.git src.main
$ cd src.main
$ git checkout 51117ed1 # Just to be sure that we are patching the same version as it was at the moment of creating the document.
$ cd ..
$ git clone https://github.com/freebsd/freebsd.git src
$ cd src
$ git checkout release/13.2.0
$ SD=$BASEDIR/src.main/sys/contrib/device-tree
$ cd sys/contrib/device-tree/src/arm64/allwinner
$ cp $SD/src/arm64/allwinner/sun50i-h616-orangepi-zero2.dts .
$ echo '12a13,15
> #define RST_BUS_EMAC 33
> #define CLK_BUS_EMAC 84
>
128c131
< compatible = "allwinner,sun50i-h616-ccu";
---
> compatible = "allwinner,sun50i-h6-ccu";
493c496
< clocks = <&ccu CLK_BUS_EMAC0>;
---
> clocks = <&ccu CLK_BUS_EMAC>;
495c498
< resets = <&ccu RST_BUS_EMAC0>;
---
> resets = <&ccu RST_BUS_EMAC>;
508c511
< compatible = "allwinner,sun50i-h616-rtc";
---
> compatible = "allwinner,sun50i-h6-rtc";
510a514
> clock-output-names = "osc32k", "osc32k-out", "iosc";' | patch $SD/src/arm64/allwinner/sun50i-h616.dtsi -o sun50i-h616.dtsi
$ cd ../../../include/dt-bindings/clock
$ cp $SD/include/dt-bindings/clock/sun50i-h616-ccu.h .
$ cp $SD/include/dt-bindings/clock/sun6i-rtc.h .
$ cp $SD/include/dt-bindings/clock/sun50i-h6-r-ccu.h .
$ cd ../reset
$ cp $SD/include/dt-bindings/reset/sun50i-h616-ccu.h .
$ cp $SD/include/dt-bindings/reset/sun50i-h6-r-ccu.h .
$ cd $BASEDIR/src/sys/modules/dtb/allwinner
$ echo '56a57
> allwinner/sun50i-h616-orangepi-zero2.dts \' | patch Makefile -o Makefile.patched
$ mv Makefile.patched Makefile
$ NFSROOT=/exports/orangepizero2-${{HOSTNAME_OF_OPI}}
$ NJOBS=$(sysctl hw.ncpu|awk '{print $2-1}')
$ cd $BASEDIR/src
$ make -j$NJOBS buildworld TARGET_ARCH=aarch64
$ make -j$NJOBS buildkernel TARGET_ARCH=aarch64 KERNCONF=GENERIC
$ sudo mkdir -p $NFSROOT
$ sudo -E make installworld TARGET_ARCH=aarch64 DESTDIR=$NFSROOT
$ sudo -E make distribution TARGET_ARCH=aarch64 DESTDIR=$NFSROOT
$ sudo -E make installkernel TARGET_ARCH=aarch64 KERNCONF=GENERIC DESTDIR=$NFSROOT
$ exit
$ exit
$ bash
$ cd
$ ssh-keygen
$ sudo bash
# NFSROOT=/exports/orangepizero2-${{HOSTNAME_OF_OPI}}
# mkdir $NFSROOT/root/.ssh
# cp .ssh/id_rsa.pub $NFSROOT/root/.ssh/authorized_keys
# cp /etc/resolv.conf $NFSROOT/etc
# cp /etc/hosts $NFSROOT/etc
# echo 'sshd_enable="YES" # We need ssh access
background_fsck="NO" # NEVER run fsck on nfs mounted partitions
nfs_client_enable="YES" # The diskless client is an NFS client
rpc_lockd_enable="YES" # Some application require file locking
rpc_statd_enable="YES" # Some application require file locking
ntpdate_enable="YES" # We need actual date/time
ntpd_enable="YES"' > $NFSROOT/etc/rc.conf
# PC_IP=$(ifconfig|grep broadcast|awk '{print $2}')
# echo "# Device Mount FStype Options Dump Pass
$PC_IP:$NFSROOT / nfs rw 0 0
proc /proc procfs rw 0 0" > $NFSROOT/etc/fstab
# cd $NFSROOT/etc/ssh
# cat sshd_config | sed 's/#PermitRootLogin no/PermitRootLogin yes/g' > sshd_config.new
# mv sshd_config.new sshd_config
# exit
$ exit
$ exit
Great! Almost everything is done in the root filesystem for our diskless Orange PI Zero2!
Let us go to prepare the Orange PI Zero2 hardware itself.
$ bash
$ cd
$ mkdir bl31
$ cd bl31
$ git clone https://github.com/ARM-software/arm-trusted-firmware.git
$ cd arm-trusted-firmware
$ git checkout v2.8.0
$ gmake realclean
$ TF_LDFLAGS="--no-warn-rwx-segment" gmake CROSS_COMPILE=aarch64-none-elf- PLAT=sun50i_h616 bl31
$ cd
$ mkdir u-boot
$ git clone https://source.denx.de/u-boot/u-boot.git
$ cd u-boot
$ git checkout v2023.04
$ gmake orangepi_zero2_defconfig
$ cp ~/bl31/arm-trusted-firmware/build/sun50i_h616/release/bl31.bin .
$ gmake CROSS_COMPILE=aarch64-none-elf-
$ echo 'bootcmd=gpio set 76;sf probe;sf erase 0 0x200000;load mmc 0 0x42000000 u-boot-sunxi-with-spl.bin;sf write 0x42000000 0 0x200000;while true;do gpio toggle 76;gpio toggle 77;sleep 1;done' > uboot.txt
$ tools/mkenvimage -s 65536 -o uboot.env uboot.txt
$ echo 'gpio set 76;gpio clear 77' > boot.scr
$ tools/mkimage -A arm -T script -d boot.scr boot.scr.uimg
$ sudo cp boot.scr.uimg /exports/orangepizero2-${{HOSTNAME_OF_OPI}}/boot
$ sudo bash
# SD=${{path_to_microSD_card_device}} #Usually on the PC with SSD hard drive it is "/dev/da0", but be carefully, doublecheck what it is on your PC!
# gpart destroy -F $SD #Here may be error if card already empty
# dd if=/dev/zero of=$SD bs=1024 count=4096
So, card is empty now and we can create everything what we need.
# gpart create -s mbr $SD # If you have an error here, return back to command "gpart destroy"
# gpart add -a 3M -t fat16 -s 20M $SD #Started from 3mbyte, leaving space for u-boot
# newfs_msdos -F 16 ${SD}s1
# dd if=u-boot-sunxi-with-spl.bin of=$SD bs=1k seek=8 conv=sync
# mount_msdosfs ${SD}s1 /mnt
# cp u-boot-sunxi-with-spl.bin /mnt
# cp uboot.env /mnt
# umount /mnt
# exit
Great! Initialisation card is ready and we can initialise our very first Orange PI Zero2.
That is very simple now, just insert the card into OPI and turn it on.
In about 10 seconds after turning OPI on we should see on OPI the red led ligting it does mean that the flash process has been started!
In about a minute after turning OPI on we should see on OPI the red and green leds are blinking, it does mean that the flash process has been finished, so, we can turn OPI off and eject the microSD card.
VERY IMPORTANT! We have to inform our OPI that it can boot now from SPI-flash.
To do that we have to install a jumper between two pin contacts: 13 and 14.
They are in the very middle of 26-pin sets.
With the jumper between 13 and 14 the OPI will try to boot from microSD card first and if
microSD card does not exists it will try to boot from SPI flash.
Ok! Congratulations! Our OPI is ready to start diskless booting!
Is that all?
Not quite...
$ sudo tcpdump -i $(ifconfig|awk '{i=$1;if((i==substr($0,1,length(i)))&&(i!="lo0:"))print substr(i,1,length(i)-1)}') port 67
It will stuck here and will display all DHCP requests which will appear in our network.
After run that command, connect the OPI (without the microSD card) to the network and turn it on.
Wait about 30 seconds and we should see on the terminal (where we started the tcpdump) something like that:
"... IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 02:00:xx:xx:xx:xx (oui Unknown), length 300"
So, the MAC address of our OPI is 6 hex numbers after "Request from".
Highly likely it will start from 02:00 but may be something else, who knows...
Ok. We have discovered the MAC address of our OPI. Write it down, we will need it in future and turn the OPI off.
Turn the OPI off and close the terminal where we started tcpdump.
$ sudo bash
# echo 'inetd_enable="YES"' >> /etc/rc.conf
# echo 'tftp dgram udp wait root /usr/libexec/tftpd tftpd -l -s /exports/orangepizero2-${{HOSTNAME_OF_OPI}}/boot' >> /etc/inetd.conf
# service inetd start
# echo 'rpcbind_enable="YES"
nfs_server_enable="YES"
mountd_enable="YES"
rpc_lockd_enable="YES"
rpc_statd_enable="YES"' >> /etc/rc.conf
# echo '/exports/orangepizero2-${{HOSTNAME_OF_OPI}} -maproot=root:wheel ${{OPI_IP}}' >> /etc/exports
# service nfsd start
# service lockd start
# exit
$ exit
host ${{HOSTNAME_OF_OPI}} {
hardware ethernet ${{MAC_ADDRESS_WHICH_WAS_DISCOVERED_IN_P4.5}};
fixed-address ${{OPI_IP}};
next-server ${{PC_IP}}; # tftp server
filename "loader.efi";
option host-name "${{HOSTNAME_OF_OPI}}";
option root-path "${{PC_IP}}:/exports/orangepizero2-${{HOSTNAME_OF_OPI}}"; #Where the root of filesystem is placed
}
Restart the dhcp server. On the CentOS 6 it can be done by command under root:Is that all now???
Yes! Eventually we are here!
Connect our Orange PI Zero2 withour microSD card to the network and turn it on.
If everything is right then in about 10 seconds we can see red light on Orange PI Zero2, taht is mean the boot process has been started.
In about a minute after the red light appear you should be able to ssh as root to diskless Orange PI Zero2!
For do that, ssh to build@${{PC_IP}} and execute the command answering "yes" on the question:
$ ssh root@${{OPI_IP}}
# pkg install bash
# bash
# echo "$(ifconfig|grep broadcast|awk '{print $2}') $(hostname)" >> /etc/hosts
Horray!! We did it!!!
Postscriptum:
If the red light is not appearing in one minute, try to turn it off and on again.
Try it two-three times.
If that still does not work, try another Orange PI Zero2.
I got the experience that some of my OPIs does not catch DHCP configuration. I do not know why.
If that still does not work, connect the serial console to the Orange PI Zero2 and see what is going on.