Skip to content

Booting Linux from SD

Brandon Perez edited this page Mar 20, 2017 · 7 revisions

Back to Home

Overview

This wiki describes how to boot a Linux kernel from an SD card on the Zedboard. The root filesystem for the kernel resides on a separate partition on the SD card.

Prerequisites

Your system must be setup according to the method outlined in System Setup.

You must have all of the boot files compiled as outlined in Build the Boot Sources

The root filesystem must be setup as outlined in Create a Root Filesystem.

System Configuration

These instructions were run and tested with the following software and hardware:

  • Laptop OS: Ubuntu 14.04 (64-bit)

  • Board OS: Ubuntu 14.04 (32-bit)

  • Board: Zedboard

  • Board Architecture: ARM

  • Xilinx Tools:

    • Vivado Version: 2015.2
    • Xilinx SDK Version: 2015.2
    • Vivado HLS Version: 2015.2
  • U-Boot Version:

    • Version: 2016.03
    • Branch: master
  • Linux

    • Version: 4.4.0
    • Branch: master

For reference, we assume that the following environment variables are defined:

export SW_DIR=<Path to your software>
export THRS=$((2 * `cat /proc/cpuinfo | grep processor | wc -l`))

Find the SD Card Device

Insert an SD card, and figure out what the device name for the SD card is. You can figure this out by running sudo blkid. If the card auto-mounts, then you can run df -hT to figure out where the SD card was mounted, and what the deivce name is. Also, running dmesg | tail will show recent kernel log messages, which can help identify the device name.

The device name will be the name you see with the numbers removed. SD cards will often appear as /dev/sdb1 or /dev/sdc1. The name of the device will be this path with the numbers removed, so /dev/sdb and /dev/sdc, respectively. Be very careful and ensure that you have the device name correct. Using the wrong device name (e.g. your hard drive's device name), could cause your system to become unbootable.

Setup the SD Card Partitions

To boot from an SD card, we need to setup two partitions. The first partition on the SD card will contain all the files necessary to boot the system (the bootloader, kernel image, device tree binary, and possibly the bitstream file for the FPGA). The second partition will contain the root filesystem for the kernel. We're going to name these partitions boot and rootfs, respectively, but their names can be arbitrary.

Once you find the device name for the SD card, put it into an environment variable (this will be referenced later):

export SD_DEV_NAME=<SD card device name>

This doesn't need to go into your .bashrc file, since it is only needed for this one-time setup. Make sure that this device number is correct, or else you may cause irreversible damage to another storage device (potentially your hard drive).

Unmount all of the current SD card partitions:

sudo umount ${SD_DEV_NAME}?

Reformat the SD card partitions table, creating a 200 MB boot partition, and using the remaining space for the root filesystem partition (NOTE: this will make all prior data on the SD card inaccessible):

sudo fdisk ${SD_DEV_NAME} << EOF
d
4
d
3
d
2
d
n
p
1

+200M
n
p
2


w
EOF

This will create two partitions on the SD card, which will appear as the devices /dev/${SD_DEV_NAME}1 and /dev/${SD_DEV_NAME}2, respectively.

Format the SD Card Partitions

For the SD card partitions to be usable, we need to setup filesystems on each of the partitions, so files can be read from and written to the SD card. The partition containing the boot files must be formatted as FAT. The root filesystem is typically EXT4, so it the other partition should be formatted as such. Linux can support other filesystem types, but EXT4 is the usual filesystem type.

Format the first partition as FAT, and name it boot:

sudo mkfs.fat -n boot ${SD_DEV_NAME}1

Format the second partition as EXT4, and name it rootfs:

sudo mkfs.ext4 -L rootfs ${SD_DEV_NAME}2

Now remove the SD card and reinsert it. The boot and rootfs partitions should automount on your system.

Copy Files to the SD Card

Make sure the SD card you used in the previous step is currently inserted, and its partitions are mounted.

Copy the bootloader image to the SD card:

cp "${SW_DIR}/boot_files/boot.bin" "/media/${USER}/boot/boot.bin"

Copy the Linux kernel image and the device tree to the SD card:

cp "${SW_DIR}/boot_files/uImage" "/media/${USER}/boot/uImage"
cp "${SW_DIR}/boot_files/zynq-zed.dtb" "/media/${USER}/boot/devicetree.dtb"

Copy the core filesystem over:

sudo cp -ar ${SW_DIR}/ubuntu_14.04_corefs/* "/media/${USER}/rootfs/"

You can also include a bitstream on the boot partition, which will be used by the bootloader to program the FPGA on bootup. This isn't required, but we include the "empty" bitstream here, which only contains the processing system IP generated while creating the FSBL. This is to demonstrate how you can have your synthesized designed programmed onto the FPGA. Copy the bitstream file over:

cp "${SW_DIR}/boot_files/system.bit" "/media/${USER}/boot/system.bit"

Unmount the SD card:

sync
umount "/media/${USER}/boot"
umount "/media/${USER}/rootfs"

Boot the System

To boot from an SD card on the Zedboard, we need to make sure the jumpers are set for the SD boot mode. The jumpers that control the boot mode are JP11-JP7 (MIO6-MIO2). They must be set in the following configuration: JP11 GND, JP10 3V3, JP9 3V3, JP8 GND, JP7 GND. Alternately, if you view it as a binary number, they should be set as 01100.

Now to communicate with the Zedboard, we need to setup a serial console between our host machine and the board. Connect a micro USB into the UART port (J14). Now, turn on the Zedboard.

The Zedboard's serial console will appear as a device from the host side. It will be /dev/ttyACM0.

Connect to the Zedboard's serial console:

sudo minicom -D /dev/ttyACM0

On the Zedboard, press the PS-RST button (BTN7) to reset the system and begin the boot. Press any key to prevent automatic booting. If the system doesn't reset, then trying powering the Zedboard off an on by toggling the On-Off switch (SW8).

Enter the following commands on the Minicom serial console (in the U-Boot shell) to boot:

env default -a
setenv modeboot sdboot
setenv bootargs console=ttyPS0,115200 root=/dev/mmcblk0p2 rw rootwait earlyprintk ignore_loglevel
boot

You should now see printk messages coming from the kernel. The userspace environment is configured to automatically log you in as the root user. You should be dropped directly into a shell.

Automatic Booting

It can quickly become very tedious to enter all of those commands every time you want to boot the kernel, especially if you're doing rapid development. Fortunately, U-Boot provides methods for overriding the default boot commands.

U-Boot saves its environment variables in the on-chip flash memory, so it has a persistent state. U-Boot allows these variables to be overwritten, and so you can change the commands that U-Boot runs for the default boot. This is controlled by the environment variables bootcmd, which contains the commands that U-Boot runs when it boots. There are other special environment variables, like bootargs, which, if set, controls the command-line arguments that are passed to the Linux kernel when it boots.

Create a boot script for booting from an SD card:

cat << "EOF" > "${SW_DIR}/config/sd_boot.script"
# Performs a SD boot with the root filesystem on the SD card
# Assumes following variables are defined:
#   kernel_image, devicetree_image, kernel_load_address, devicetree_load_address, loadbit_addr, filesize

# Reset the environment to its default settings
env default -a

# Set the name of the bitstream file
setenv bitstream_image 'system.bit'
setenv bitstream_load_address "${loadbit_addr}"

# Set the console and root filesystem we're going to use
setenv console 'ttyPS0,115200'
setenv root '/dev/mmcblk0p2'

# Dynamically set the boot arguments for the kernel (reads the environment variables)
setenv set_bootargs 'setenv bootargs console=${console} root=${root} rw rootwait earlyprintk ignore_loglevel'

# If it exists, load the bitstream into memory and then program the PL
setenv print_fpga_success 'echo Programming FPGA with ${bitstream_image} on the SD card...'
setenv print_fpga_failure 'echo No ${bitstream_image} found on the SD card, skipping FPGA programming.'
setenv load_fpga 'load mmc 0 ${bitstream_load_address} ${bitstream_image}'
setenv program_fpga 'fpga loadb 0 ${bitstream_load_address} ${filesize}'
setenv do_program_fpga 'if run load_fpga program_fpga; then run print_fpga_success; else run print_fpga_failure; fi'

# Load Linux and the device tree into memory
setenv print_kernel 'echo Loading the kernel from ${kernel_image} on the SD card...'
setenv load_kernel 'load mmc 0 ${kernel_load_address} ${kernel_image}'
setenv do_load_kernel 'run print_kernel load_kernel'
setenv print_devicetree 'echo Loading the device tree from ${devicetree_image} on the SD card...'
setenv load_devicetree 'load mmc 0 ${devicetree_load_address} ${devicetree_image}'
setenv do_load_devicetree 'run print_devicetree load_devicetree'

# Set the command we're using to boot
setenv print_boot_message 'echo Booting Linux from SD with an root SD filesystem...'
setenv boot_kernel 'bootm ${kernel_load_address} - ${devicetree_load_address}'
setenv bootcmd 'run print_boot_message set_bootargs do_program_fpga do_load_kernel do_load_devicetree boot_kernel'

# Save the current environment in persistent storage, and reset
saveenv
reset
EOF

Copy and paste these commands into the U-Boot console, after restarting the system and stopping automatic booting. This will reset the system, then you should see the system boot, and then be dropped into a shell. Now, everytime the system starts up, it will automatically boot the Linux kernel from the SD card.

Note: The comments are in the script simply to explain, but they can sometimes cause issues when copy and pasting into the U-Boot console. It is recommended that you remove the comments from the boot script file that was created.

Next Steps

Now that you have the Linux kernel booting from an SD card, you can try alternate methods for booting, that will make development time quicker.

For booting with a networked root filesystem, see Booting Linux with NFS Root.

For booting Linux over TFTP (trivial file transfer protocol) with a networked root filesystem, see Booting Linux with TFTP.

References

The instructions for booting from SD were adapted from the Texas Instruments J6 Wiki - Wiki

Files

zynq_fsbl.elf - Prebuilt FSBL generated from the steps on the wiki.

u-boot.elf - Prebuilt U-Boot binary from the Xilinx U-Boot repostiory.

boot.bin - Prebuilt bootloader image, which is the result of running bootgen on the FSBL and U-Boot binaries.

system.bit - "Empty" bitstream file for programming the FPGA, generated from the steps on the wiki.

uImage - Prebuilt Linux kernel image from the Xilinx Linux repository.

devicetree.dtb - Prebuilt device tree binary for the Zedboard from the Xilinx Linux repository.