April 18, 2024

EBAZ4205 Bitcoin miner board - Booting (part 1)

I have always been fascinated by how systems boot, so I am going to dig into a lot of detail. This is just a warm up.

We followed the recipe below to boot the system up and change the root password. Let's look at this in some detail rather than just using it as a black box recipe. The first line is a bunch of commands glued together. Taking that line apart and analyzing it would be our first step.

We typed "d" to interrupt the normal boot and get to the U-boot prompt, then did this:

setenv nandboot "echo Copying Linux from NAND flash to RAM... && nand info && run nandroot;nand read 0x100000 0x2220000 0x300000 && fpga loadb 0 0x100000 0x300000 && nand read ${kernel_load_address} 0x300000 ${kernel_size} && nand read ${devicetree_load_address} 0x800000 ${devicetree_size}"
run nandboot
setenv bootargs 'console=ttyPS0,115200 root=/dev/mtdblock6 rootfstype=jffs2 noinitrd rw rootwait reboot=cold,hard emergency init=/bin/sh'
bootm ${kernel_load_address} - ${devicetree_load_address} init=/bin/sh
Let's unfold the first line. I packs a bunch of commands into the "nandboot" variable, then tells U-boot to run that as a command:
echo Copying Linux from NAND flash to RAM...
nand info
run nandroot
nand read 0x100000 0x2220000 0x300000
fpga loadb 0 0x100000 0x300000
nand read ${kernel_load_address} 0x300000 ${kernel_size}
nand read ${devicetree_load_address} 0x800000 ${devicetree_size}
run nandboot
The first two lines are just cosmetic -- they print a cute message, then some info about the NAND setup.
Then we run the "nandroot" (not nandboot) command, which is already set up in the U-boot environment as:
zynq-uboot> printenv nandroot
nandroot=setenv bootargs 'console=ttyPS0,115200 root=/dev/mtdblock6 rootfstype=jffs2 noinitrd rw rootwait'
So, all doing "run nandroot" does is to run a command that sets the value of the bootargs variable. This is (as near as I can tell) entirely pointless as the script will overwrite that when it sets bootargs with some adjusted values later.

Typing "help nand" and "help fpga" give details about the subsequent commands.
Here is my analysis, one by one.

nand read 0x100000 0x2220000 0x300000
The above reads into ram at 0x100000 from nand offset 0x2220000 for 0x300000 bytes.
fpga loadb 0 0x100000 0x300000
The above loades the FPGA device from a bitstream buffer (apparently the stuff we just read from memory in the previous command. It is not clear to me that we really need to load the FPGA just to boot and run linux to do the password reset.
nand read ${kernel_load_address} 0x300000 ${kernel_size}
nand read ${devicetree_load_address} 0x800000 ${devicetree_size}
Again, these two commands read blocks of information from NAND into ram. The addresses are part of the existing U-boot setup as follows:
kernel_load_address=0x2080000
kernel_size=0x500000
devicetree_load_address=0x2000000
devicetree_size=0x20000
Once these two regions have been read, we are ready to issue the "bootm" command, but with a modified setup for the bootargs variable.
setenv bootargs 'console=ttyPS0,115200 root=/dev/mtdblock6 rootfstype=jffs2 noinitrd rw rootwait reboot=cold,hard emergency init=/bin/sh'
bootm ${kernel_load_address} - ${devicetree_load_address} init=/bin/sh
The bootm command boots an application stored in memory, passing arguments.
While it looks like it is doing math (subtracting two addresses), it is not. Apparently the "-" is just a place holder -- and this is documented. The second argument could be the address of a ramdisk image.

Now we need to do some experimenting. I try just the following 4 commands:

nand read ${kernel_load_address} 0x300000 ${kernel_size}
nand read ${devicetree_load_address} 0x800000 ${devicetree_size}
setenv bootargs 'console=ttyPS0,115200 root=/dev/mtdblock6 rootfstype=jffs2 noinitrd rw rootwait reboot=cold,hard emergency init=/bin/sh'
bootm ${kernel_load_address} - ${devicetree_load_address}
And it works! This boots the kernel and gives me a shell running in place of "init"!!

I see the following messages (among many others) pass by:

## Booting kernel from Legacy Image at 02080000 ...
   Image Name:   Linux-4.6.0-xilinx
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    3820688 Bytes = 3.6 MiB
   Load Address: 00008000
   Entry Point:  00008000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 02000000
   Booting using the fdt blob at 0x2000000
   Loading Kernel Image ... OK
   Loading Device Tree to 0f2fe000, end 0f303b90 ... OK

Kernel command line: console=ttyPS0,115200 root=/dev/mtdblock6 rootfstype=jffs2 noinitrd rw rootwait reboot=cold,hard emergency init=/bin/sh
So, there is confirmation that all the stuff in the bootargs env variable got passed along to the kernel, and about the addresses used for the kernel itself and the FDT (flattened device tree).

Enough for this page.


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org