Introduction: How to Boot Linux on a Zedboard Without U-Boot
Usually, the startup sequence for Linux on a Zedboard is:
- The First Stage Boot Loader (FSBL) in the Zynq ROM reads the boot.bin file from the boot media, such as the SD Card
- It passes control to the FSBL in the boot.bin file
- Which then passes control to the U-Boot boot loader, which may read additional files from the boot media
- U-Boot sets the architecture register (r2) and then jumps to zImage
- zImage decompresses the kernel Image and then jumps to the Image's start address
- and then Linux boots
U-Boot adds two capabilities to the boot process that the Xilinx FSBL does not have. (1) It reads files used at boot time from the SD Card instead of requiring them to be included in the boot.bin, and (2) it has a command line, in case you want to modify boot parameters at boot time.
If you do not need either of those capabilities, then booting Linux directly from FSBL saves some time during boot and reduces the number of chip/board specific files that need to be maintained.
In our configurations, we have only three chip/board specific files:
- zynq_fsbl.elf
- devicetree.dtb
- the bitfile to program the FPGA
We only include the first two in the boot.bin file and load the third from the filesystem after Linux boots.
In this Instructable, we show how to boot Linux on a Zedboard directly from the Xilinx FSBL.
Step 1: What You Will Need
- A Zedboard
- An SD Card
- A Linux machine, Linux VM, or Mac
- A git client, and some knowledge of how to use it.
- Android NDK version r9d or newer
Step 2: Zynq-Boot
Downloading Zynq-Boot
First, let's download the code for zynq-boot, which contains what we need to set up a Zedboard to boot Android
git clone git://github.com/cambridgehackers/zynq-boot
What a boot loader needs to do to boot Linux
A boot loader needs to do just a few things before it can boot ARM Linux.
- Configure the DRAM controller
- Load the zImage and any initial ramdisk image into DRAM
- Load the machine type value into r2
- Jump to the zImage
By packing the zImage and initial ramdisk image into a boot.bin file, Xilinx's fsbl takes care of the first two of these. So all we need to do is the last 2.
Except the Xilinx kernel seems to require some other configuration registers to be initialized. Over time, perhaps this configuration wil be moved to the kernel to simplify this process.
How Zynq-Boot prepares the way for Linux
If we take a look at the contents of Zynq-Boot, one of the files of interest is clearreg.S. This file is compiled and linked with zImage so that this code is the entry point.
It starts with some code to jump past a data table:
.arm .data .global clearreg clearreg: adr r4, data_table b 3f
Then it contains the data table itself. Each entry is two 32-bit words: a configuration value to write and an address at which to write it. These registers could be configured by the Xilinx FSBL or by the Linux kernel, making this step easier.
data_table: .word SLCR_UNLOCK_MAGIC, XPSS_SYS_CTRL_BASEADDR+0x8//slcr_unlock /* remap DDR to zero, FILTERSTART */ .word 0, XPSS_SCU_BASEADDR + 0x40 //filter_start /* Device config APB, unlock the PCAP */ .word 0x757BDF0D, XPSS_DEV_CFG_APB_BASEADDR + 0x34 //unlock .word 0xFFFFFFFF, XPSS_DEV_CFG_APB_BASEADDR + 0x28 //rom_shadow /* OCM_CFG, Mask out the ROM, map ram into upper addresses */ .word 0x1F, XPSS_SYS_CTRL_BASEADDR + 0x910 //ocm_cfg /* FPGA_RST_CTRL, clear resets on AXI fabric ports */ .word 0x0, XPSS_SYS_CTRL_BASEADDR + 0x240 //fpga_rst_ctrl /* TZ_DDR_RAM, Set DDR trust zone non-secure */ .word 0xFFFFFFFF, XPSS_SYS_CTRL_BASEADDR + 0x430 //trust_zone /* Set urgent bits with register */ .word 0x0, XPSS_SYS_CTRL_BASEADDR + 0x61c //ddr_urgent_sel /* Urgent write, ports S2/S3 */ .word 0xC, XPSS_SYS_CTRL_BASEADDR + 0x600 //ddr_urgent .word SLCR_LOCK_MAGIC,XPSS_SYS_CTRL_BASEADDR + 0x4 //slcr_lock .word 0, 0 // end of table
After the data table is the machine type:
machine_type: .word 0xd32 // XILINX_EP107 (Zynq) = 3378.
Then there is the code that configures the processor according to the data table:
2: str r0, [r1] 3: ldmia r4!, {r0,r1} cmp r1, #0 bne 2b
Finally there is the code that loads the XILINX_EP107 machine type and continues to the zImage entry point:
ldr r1, machine_type mov r2,#0x1000000 // set address of devicetree data // fall through (to start of zImage)
After that, Linux boots as usual.
Step 3: Making Bootbin.zedboard
We could make the SD card image all in one step, but if you're reading this, you are probably interested in some of the details.
First, make sure the Native Development Kit is on your PATH:
jamey@sj6:/scratch/jamey/zynq-boot$ PATH=$PATH:/scratch/android-ndk-r9d
You can test this with ndk-which:
jamey@sj6:/scratch/jamey/zynq-boot$ ndk-which gcc /scratch/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc
Next, we will make the boot.bin file for the zedboard:
jamey@sj6:/scratch/jamey/zynq-boot$ make bootbin.zedboard make: Entering directory `/scratch/jamey/zynq-boot' make[1]: Entering directory `/scratch/jamey/zynq-boot' making ramdisk 545 blocks 256+0 records in 512+0 records out making dtb.tmp local-mac-address = [ 00 E0 0C 00 96 03 ]; compiling device tree making zcomposite.elf making real.bootbin `boot.bin' -> `boot.bin.bak' `imagefiles/zynq_zedboard_fsbl.elf' -> `zynq_fsbl.elf' make[1]: Leaving directory `/scratch/jamey/zynq-boot' make: Leaving directory `/scratch/jamey/zynq-boot'
There is some chatter from the different steps. One thing to notice is that it assigns the ethernet MAC address based on $(USER) and $(BOARD). If you're using multiple boards of the same type, you can obtain unique MAC addresses by using alternate definitions of $(USER)
jamey@sj6:/scratch/jamey/zynq-boot$ make USER=jamey1 bootbin.zedboard ... local-mac-address = [ 00 E0 0C 00 e5 03 ]; ...
Step 4: Making Sdcard.zedboard
We need more than boot.bin in order to boot Android on the zedboard.
This step creates the other files needed to boot Android on the zedboard and puts them in the sdcard-zedboard directory.
jamey@sj6:/scratch/jamey/zynq-boot$ make sdcard.zedboard make[1]: Entering directory `/scratch/jamey/zynq-boot' downloading /system filesystem image ... ... ... system.img: OK `imagefiles/zynqportal.ko' -> `sdcard-zedboard/zynqportal.ko' `imagefiles/portalmem.ko' -> `sdcard-zedboard/portalmem.ko' `imagefiles/timelimit' -> `sdcard-zedboard/timelimit' Files for zedboard SD Card are in /scratch/jamey/zynq-boot/sdcard-zedboard make[1]: Leaving directory `/scratch/jamey/zynq-boot'
Step 5: Copying the Files to the SD Card
The files are in directory sdcard-zedboard:
jamey@sj6:/scratch/jamey/zynq-boot$ ls -l sdcard-zedboard total 269288 drwxrwxr-x 2 jamey users 4096 Oct 15 12:13 3.9.0-00054-g7b6edac -rw-rw-r-- 1 jamey users 2664148 Oct 15 12:13 boot.bin -rw-rw-r-- 1 jamey users 14084 Oct 15 12:15 portalmem.ko -rw-rw-r-- 1 jamey users 268435456 Oct 15 12:15 system.img -rwxrwxr-x 1 jamey users 13704 Oct 15 12:15 timelimit -rw-rw-r-- 1 jamey users 104857600 Oct 15 12:13 userdata.img -rw-rw-r-- 1 jamey users 15342 Oct 15 12:15 zynqportal.ko
Copy these to an SD card formatted in the standard way (MS DOS filesystem, one partition).
Step 6: Booting the Zedboard
Insert the SD Card into the Zedboard and turn it on. If you want to see the console output, you will need a terminal emulator connected to the USB "UART" port. You will need to plug in ethernet so that you can communicate with the zedboard over TCP.
Booting Linux on physical CPU 0x0 Linux version 3.9.0-00053-gb605a94-dirty (jamey@sj6) (gcc version 4.3.3 (Sourcery G++ Lite 2009q1-203) ) #2 SMP PREEMPT Tue Aug 5 08:56:30 EDT 2014 CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=18c5387d CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache Machine: Xilinx Zynq Platform, model: Xilinx Zynq ZC702 cma: CMA: reserved 16 MiB at 2e800000 Memory policy: ECC disabled, Data cache writealloc PERCPU: Embedded 7 pages/cpu @c180f000 s8128 r8192 d12352 u32768 Built 1 zonelists in Zone order, mobility grouping on. Total pages: 260624 Kernel command line: console=ttyPS0,115200 initrd=0x00800000,256K noinitrd init=/init root=/dev/ram rw ip=:::::eth0:dhcp earlyprintk PID hash table entries: 4096 (order: 2, 16384 bytes) Dentry cache hash table entries: 131072 (order: 7, 524288 bytes) Inode-cache hash table entries: 65536 (order: 6, 262144 bytes) __ex_table already sorted, skipping sort .... mmc0: new high speed SDHC card at address e624<br>mmcblk0: mmc0:e624 SU04G 3.69 GiB mmcblk0: p1 Sending DHCP requests ..xemacps e000b000.eth: Set clk to 124999998 Hz xemacps e000b000.eth: link up (1000/FULL) ., OK IP-Config: Got DHCP answer from 172.18.0.11, my address is 172.18.0.232 IP-Config: Complete: device=eth0, hwaddr=00:e0:0c:00:98:09, ipaddr=172.18.0.232, mask=255.255.240.0, gw=172.18.0.1 host=172.18.0.232, domain=qrclab.com, nis-domain=(none) bootserver=172.18.0.11, rootserver=172.18.0.11, rootpath= nameserver0=172.17.0.111, nameserver1=172.17.0.101 Freeing init memory: 148K init (1): /proc/1/oom_adj is deprecated, please use /proc/1/oom_score_adj instead.�!TQµfs (mmcblk0p1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.�¡EEj2Í(loop0): recovery complete�¡EEj2Í(loop0): mounted filesystem with ordered data mode. Opts: (null)�¡EEj2Í(loop1): recovery complete�¡EEj2Í(loop1): mounted filesystem with ordered data mode. Opts: (null)�init: cannot open '/initlogo.rle'�¡Ë+‹é cannot find '/system/bin/dbus-daemon', disabling 'dbus'�¡Ë+‹é cannot find '/system/etc/install-recovery.sh', disabling 'flash_recovery'�warning: `adbd' uses 32-bit capabilities (legacy support in use) / $ [6nfs_mgr: Cannot open file /fstab.xilinxzynqplatform / $ ifconfig eth0 eth0 Link encap:Ethernet HWaddr 00:E0:0C:00:96:03 inet addr:172.18.0.232 Bcast:172.18.15.255 Mask:255.255.240.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:121 errors:0 dropped:0 overruns:0 frame:0 TX packets:6 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:7826 (7.6 KiB) TX bytes:1916 (1.8 KiB) Interrupt:54 Base address:0xb000