Introduction: How to Boot Linux on a Zedboard Without U-Boot

Usually, the startup sequence for Linux on a Zedboard is:

  1. The First Stage Boot Loader (FSBL) in the Zynq ROM reads the boot.bin file from the boot media, such as the SD Card
  2. It passes control to the FSBL in the boot.bin file
  3. Which then passes control to the U-Boot boot loader, which may read additional files from the boot media
  4. U-Boot sets the architecture register (r2) and then jumps to zImage
  5. zImage decompresses the kernel Image and then jumps to the Image's start address
  6. 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:

  1. zynq_fsbl.elf
  2. devicetree.dtb
  3. 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://

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.

  1. Configure the DRAM controller
  2. Load the zImage and any initial ramdisk image into DRAM
  3. Load the machine type value into r2
  4. 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
        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.

	/* 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 0,            0                              // end of table

After the data table is the 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

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, my address is
IP-Config: Complete:
     device=eth0, hwaddr=00:e0:0c:00:98:09, ipaddr=, mask=, gw=
     host=,, nis-domain=(none)
     bootserver=, rootserver=, rootpath=
     nameserver0=, nameserver1=
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/', 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:  Bcast:  Mask:
          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