Manuel Malagon

Guide to flash iMX6 from Linux

Discussion created by Manuel Malagon on Dec 26, 2016
Latest reply on Jan 27, 2018 by colinhelliwell

Guide to flash iMX6 from Linux

 

Hello community

 

  I will leave here this guide to flash your iMX6 board if you are working on a Linux PC and don't have access to a computer with Windows or you just simply don't want to use windows and the Freescale Manufacturing Tool. It is also useful for stop using the windows manufacturing tool and to work completely from Linux.

 

  To achieve this we will use two very important utilities, theimx_usb_loader and theutp_com utilities. Also, it is assumed that you know how to create your board images with yocto and you have done so. Also, it is recommended that you download the windows Mfg tool and have the ucl2.xml file at hand because this file is a good reference about how to flash the board.

 

  If you are familiar with and know how to use the Windows Manufacturing tool then I recommend you to open the ucl2.xml file and compare this guide with that file for you to see how this works.

 

 

 

What is the imx_usb_loader utility for?

  The imx_usb_loader is a utility that will take the Mfg images created by yocto and transfer them to the board RAM using the Serial Downloader Protocol (SDP). After this utility transfers the manufacturing image to the board, then the board will boot into this Mfg Linux image in RAM and then the board will enumerate as a SCSI generic device in your Linux machine. The board can be found under the /dev/ folder with the name sg* (/dev/sg0 in my case). Nevertheless, this name could be different on your system so I recommend that you analyze your /dev folder looking for changes before and after you run the imx_usb_loader to identify what name does your system give to the board.

 

  What the imx_usb_loader utility does is equivalent to what the Mfg tool does with all the CMD state="BootStrap" commands found in the ucl2.xml file.

 

 

 

What is the utp_com utility for?

  Once the board has booted into the Mfg Linux image then it will start accepting UTP commands and here comes the second part, the actual images flashing. With the utp_com utility, you can manipulate the board seen as a USB SCSI device on your Linux machine. You can run bash commands from your terminal without having to actually type anything directly on the board, and the board won't let you anyways. This way you can run sfdisk for example to see your board's flash partitions and to partition your flash. You run UTP commands on the board typing them on your Linux PC as follows:

 

./utp_com -d /dev/sg0 -c "$ sfdisk -l /dev/mmcblk3"

 

  Assuming that your board flash is named mmcblk3. And you'll see the result of running this command on the serial terminal attached to your board. It is a bit awkward to type on your PC and having to see the results on the board terminal but it is what it is.

 

  What the utp_com utility does is equivalent to what the Mfg tool does with all the CMD state="Updater" commands found in the ucl2.xml file.

 

 

 

Step 1. - Get and compile the "imx_usb_loader" and the "utp_com" utilities

  The first you have to do is get and compile the utilities. For that, you just run the following commands. For the imx_usb_loader utility:

sudo apt-get install libusb-1.0-0-dev -y
git clone https://github.com/boundarydevices/imx_usb_loader.git
cd imx_usb_loader
make

And for the utp_com utility:

sudo apt-get install libsgutils2-dev -y
git clone https://github.com/ixonos/utp_com.git
cd utp_com
make

 

  I recommend you to do this in your home directory for you to have the folders "~/imx_usb_loader" and "~/utp_com" at the end. This will facilitate the creation of a script for flashing the board and not having to type all of this manually.

 

 

Step 2. - Modify the "mx6_usb_work.conf" file

  Inside the imx_usb_loader folder, it is a file called mx6_usb_work.conf that is used by the utility to transfer the Mfg Linux image to the board. The following modifications are what I ended up doing after experimenting because the actual utility documentation does not contain any information about this and the little information on the README is very unclear and incomplete. The thing is that the imx_usb_loader utility knows what config file to use according to the configurations found in the imx_usb.conf file using a configured VID:PID pair. On this file, you should see a line with your board's VID:PID and a related "mx6_usb_work.conf" file. If you don't see your VID:PID pair then add it. Actually, you can name the .conf file whatever you want as long as you configure the imx_usb.conf file correctly but we'll use the one that comes with the tool because that makes things easier.

 

  The original mx6_usb_work.conf file contents are very confusing, so I recommend you to erase everything because not all is needed. After erasing all the contents the following should be placed on the file. These are the contents if you are working with the iMX6 Solo X sabre board sd:

imx6-sabresd
#hid/bulk,[old_header,]max packet size, {ram start, ram size}(repeat valid ram areas)
hid,1024,0x10000000,1G,0x00907000,0x31000
# Note; only dcd as first step (setup ram)
firmware/u-boot-imx6sxsabresd-mfgtool.imx:dcd
# Then load a kernel with initramfs (self contained linux) at known address
firmware/zImage-mfgtool-imx6sxsabresd.bin:load 0x80800000
firmware/fsl-image-mfgtool-initramfs-imx6sxsabresd.cpio.gz.u-boot: load 0x83800000
firmware/zImage-mfgtool-imx6sx-sdb.dtb: load 0x83000000
# Now, we load a u-boot that expects preloaded software at known adress
# Note, to make this boot, the clear_dcd field seems necessary
firmware/u-boot-imx6sxsabresd-mfgtool.imx:clear_dcd,load,plug,jump header

  The "load" addresses are taken from the ucl2.xml file. As you can see in the code above is that the files are specified to be in a folder called "firmware". This means that we have to copy the Mfg files from the build/tmp/deploy/images/imx6sxsabresd to a file called firmware inside the imx_usb_loader folder on home. This because when I tried this I realized that for some reason the imx_usb_loader was not able to grab the files from their original folder, I don't know if it's something to do with the length of the path but it seems that that is the reason. Anyways, copying the files solves the problem.

 

 

Step 3. - Creating the scripts to flash the board

  The final step is to create a couple of scripts to flash the board. This step is very helpful for not having to manually run all the commands and copying the files from the images folder to the firmware folder and so forth. Therefore the next scripts are very helpful to do all that automatically. Place both scripts in your project root folder, for example for the imx6 sabre sd board I have the folder fsl-release-bsp, therefore I would place both scripts into this folder.

 

First is the script to transfer the manufacturing image to the board's RAM.

This script verifies that both the imx_usb_loader and the upt_com utilities are present on the system. It also helps to copy the Mfg files from the original "images" folder to a "firmware" folder created inside the imx_usb_loader folder. It copies the files and after their transfer to the board, it deletes the folder. Finally, the script runs another script that has all the operations to be done on the board flash using UTP commands.

 

  It is better to have the utp commands script as a separate set of commands from the imx_usb_loader script because you will be needing several versions of this UTP commands file to achieve different results. The following file I will call it "board-flash.sh"

#!/bin/bash

# p1.- imx_loader and utp_com path
# p2.- Flashing script with the UTP commands

RED='\e[1;37;41m'
GREEN='\e[1;37;42m'
YELLOW='\e[1;33m'
NC='\e[0m'

WORKING_DIR=`pwd`
IMX_USB_LOADER_NAME=imx_usb
UTP_COM_NAME=utp_com

# Search for the paths where imx USB loader and UTP com apps are located
IMX_USB_PATH=$1/imx_usb_loader

if [ ! -x $IMX_USB_PATH/$IMX_USB_LOADER_NAME ]
then
     echo -e "${RED}-imx_usb_loader- application not found!${NC}"
     echo -e "${YELLOW}Make sure that the <imx_usb_loader/imx_usb> app exists in the specified path${NC}"
     exit 1  # fail
fi

UTP_COM_PATH=$1/utp_com

if [ ! -x $UTP_COM_PATH/$UTP_COM_NAME ]
then
     echo -e "${RED}-utp_com- application not found!${NC}"
     echo -e "${YELLOW}Make sure that the <utp_com/utp_com> app exists in the specified path${NC}"
     exit 1  # fail
fi

# Establish the location of the files that will be flashed to the board
FLASH_IMAGE_DIR=$WORKING_DIR/build/tmp/deploy/images/imx6sxsabresd
MFG_IMAGE_DIR=$FLASH_IMAGE_DIR

if [[ -x ./$2 ]]
then
     APP_FLASHING_SCRIPT=$2
else
     echo -e "${RED}Invalid second input parameter${NC}"
     echo -e "${YELLOW}It should be the name of the flashing script to execute${NC}"
     echo -e "${YELLOW}Check that the second parameter is an executable bash script${NC}"
     exit 1  # fail
fi

# Go to the imx_usb_loader folder
cd $IMX_USB_PATH

# Create the folder to transfer the Mfg files from their original location
mkdir firmware

# Here we get a copy of the /dev folder looking for "sg" devices (SCSI devices)
ls /dev/sg* | grep "sg" > firmware/dev-temp1.txt

IMX_USB_PRINT=`./imx_usb 2>&1`

if `echo "$IMX_USB_PRINT" | grep -q "Could not open device"`; then
     echo -e "${RED}imx_usb returned error: Could not open device${NC}"
     echo -e "${YELLOW}Try disconnecting and reconnecting the device and run this script again${NC}"
     exit 1
fi

if `echo "$IMX_USB_PRINT" | grep -q "no matching USB device found"`; then
     echo -e "${RED}imx_usb returned error: No matching USB device found${NC}"
     echo -e "${YELLOW}Please make sure the board is connected to the USB port and the jumper is set to 'serial downloader mode'${NC}"
     exit 1
fi

if `echo "$IMX_USB_PRINT" | grep -q "err=-"`; then
     echo -e "${RED}imx_usb returned error:${NC}"
     echo $IMX_USB_PRINT
     exit 1
fi

# Copy the mfg files from the 'images' folder to the imx_usb_loader folder.
cp $MFG_IMAGE_DIR/u-boot-imx6sxsabresd-mfgtool.imx firmware/u-boot-imx6sxsabresd-mfgtool.imx
cp $MFG_IMAGE_DIR/zImage_mfgtool firmware/zImage-mfgtool-imx6sxsabresd.bin
cp $MFG_IMAGE_DIR/zImage-mfgtool-imx6sx-sdb.dtb firmware/zImage-mfgtool-imx6sx-sdb.dtb
cp $MFG_IMAGE_DIR/fsl-image-mfgtool-initramfs-imx6sxsabresd.cpio.gz.u-boot firmware/fsl-image-mfgtool-initramfs-imx6sxsabresd.cpio.gz.u-boot

# Execute imx_usb_loader to load into the board RAM the flashing OS
./imx_usb

echo "Getting the SG devices to obtain the SG device name of the board in UTP mode"
sleep 6
ls /dev/sg* | grep "sg" > firmware/dev-temp2.txt

# Get the SG device corresponding to the board by comparing the contents of /dev before
# and after our board is enumerated as a SCSI device.
DEVICE=`diff firmware/dev-temp1.txt firmware/dev-temp2.txt | grep '/dev/sg' | cut -c 3-`

# Delete the temporary files used
rm -rf firmware/

# Return to the project folder and call the script with the UTP commands
cd $WORKING_DIR
./$APP_FLASHING_SCRIPT $UTP_COM_PATH $DEVICE $WORKING_DIR $FLASH_IMAGE_DIR

The contents of the script are self-explanatory but in a nutshell, it does the following:

  • Verifies that the imx_usb_loader and the utp_com utilities executables are in the path entered as the second parameter. Here is the justification of placing both project folders at the same location.
  • Copies the Mfg files from their original location to a newly created folder called "firmware" inside the imx_usb_loader folder
  • Executes the imx_usb utility without any parameters. This causes the utility to use the "mx6_usb_work.conf" file that we modified.
  • Waits 6 seconds for the transfer to board RAM and to have some time for the Mfg Linux to boot on the board and that Linux registers the SCSI device.
  • Deletes the "firmware" folder and its contents. These files are not needed anymore after they are transferred to the board RAM
  • Finally, runs the specified script that contains the UTP commands to run to flash the board.

 

As you can see there are a couple of hard-coded items on the script that can be optimized, also, it's not very robust. Feel free to adapt the script however you want, or if you think about creating a better one, more universal, be my guest and please share if you do so.

 

Finally, the script with the UTP commands

The final script contains all the UTP commands that will be executed on the board to achieve your image flashing. I'll be using the commands for the iMX6SX found in the ucl2.xml file that comes originally with the windows Mfg tool as an example of how to create this file. If you already have a .xml file you are using that will help a lot because you just have to adapt it. The script contents are as follows and I will call the file "utp-flash-commands.sh"

#!/bin/bash

RED='\e[1;37;41m'
GREEN='\e[1;37;42m'
YELLOW='\e[1;33m'
NC='\e[0m'

# Use the UTP_COM utility to flash the application images
cd $1

# Create Partition
echo -e "${YELLOW}-> Sending partitioning shell script${NC}"
./utp_com -d $2 -c "send" -f ${3}/mksdcard.sh.tar
echo -e "${YELLOW}-> Decompresing script...${NC}"
./utp_com -d $2 -c "$ tar xf \$FILE"
echo -e "${YELLOW}-> Partitioning eMMC${NC}"
./utp_com -d $2 -c "$ sh mksdcard.sh /dev/mmcblk3"

# Setup u-boot partition
echo -e "${YELLOW}-> Access boot partition${NC}"
./utp_com -d $2 -c "$ echo 0 > /sys/block/mmcblk3boot0/force_ro"
echo -e "${YELLOW}-> Sending u-boot.bin${NC}"
./utp_com -d $2 -c "send" -f ${4}/u-boot-imx6sxsabresd.imx
echo -e "${YELLOW}-> Write u-boot into eMMC${NC}"
./utp_com -d $2 -c "$ dd if=\$FILE of=/dev/mmcblk3boot0 bs=512 seek=2"
echo -e "${YELLOW}-> Re-enable read-only${NC}"
./utp_com -d $2 -c "$ echo 1 > /sys/block/mmcblk3boot0/force_ro"
echo -e "${YELLOW}-> Enable boot partion 1 to boot${NC}"
./utp_com -d $2 -c "$ mmc bootpart enable 1 1 /dev/mmcblk3"

# Create FAT partition
echo -e "${YELLOW}-> Waiting for the partition ready${NC}"
./utp_com -d $2 -c "$ while [ ! -e /dev/mmcblk3p1 ]; do sleep 1; echo \"waiting...\"; done "
echo -e "${YELLOW}-> Formatting zImage partition p1${NC}"
./utp_com -d $2 -c "$ mkfs.vfat /dev/mmcblk3p1"
./utp_com -d $2 -c "$ mkdir -p /mnt/mmcblk3p1"
./utp_com -d $2 -c "$ mount -t vfat /dev/mmcblk3p1 /mnt/mmcblk3p1"

# Burn zImage (Kernel) on p1
echo -e "${YELLOW}-> Sending kernel zImage${NC}"
./utp_com -d $2 -c "send" -f ${4}/zImage
echo -e "${YELLOW}-> Write kernel image to eMMC card @ p1${NC}"
./utp_com -d $2 -c "$ cp \$FILE /mnt/mmcblk3p1/zImage"

# Burn dtb on p1
echo -e "${YELLOW}-> Sending Device Tree file${NC}"
./utp_com -d $2 -c "send" -f ${4}/zImage-imx6sx-sdb.dtb
echo -e "${YELLOW}-> Writing device tree file to sd card @ p1${NC}"
./utp_com -d $2 -c "$ cp \$FILE /mnt/mmcblk3p1/zImage-imx6sx-sdb.dtb"
echo -e "${YELLOW}-> Unmounting vfat partition${NC}"
./utp_com -d $2 -c "$ umount /mnt/mmcblk3p1"

# Populate default rootfs on p2
echo -e "${YELLOW}-> Formatting rootfs partition @ p2${NC}"
./utp_com -d $2 -c " mkfs.ext3 -F -E nodiscard /dev/mmcblk3p2"
./utp_com -d $2 -c " mkdir -p /mnt/mmcblk3p2"
./utp_com -d $2 -c " mount -t ext3 /dev/mmcblk3p2 /mnt/mmcblk3p2"
echo -e "${YELLOW}-> Sending and Flashing rootfs partition to sd card @ p2${NC}"
./utp_com -d $2 -c "pipe dd of=/dev/mmcblk3p2 bs=1M" -f ${4}/core-image-minimal-imx6sxsabresd.ext4
echo -e "${YELLOW}-> Finishing rootfs image write on p2${NC}"
./utp_com -d $2 -c "frf"
echo -e "${YELLOW}-> Unmounting rootfs partition${NC}"
./utp_com -d $2 -c "$ umount /mnt/mmcblk3p2"

# Done
echo "   "
echo -e "${GREEN}                            ${NC}"
echo -e "${GREEN} -> Board Setup Complete <- ${NC}"
echo -e "${GREEN}                            ${NC}"
echo "   "
./utp_com -d $2 -c "$ echo Update Ready"

 

That file is the equivalent to the following .xml file

<LIST name="eMMC" desc="Choose eMMC as media">

  <!-- Create partition -->
  <CMD state="Updater" type="push" body="send" file="mksdcard.sh.tar">Sending partition shell script</CMD>
  <CMD state="Updater" type="push" body="$ tar xf $FILE "> Decompresing script...</CMD>
  <CMD state="Updater" type="push" body="$ sh mksdcard.sh /dev/mmcblk3"> Partitioning eMMC</CMD>

  <!-- Setup boot partition -->
  <CMD state="Updater" type="push" body="$ echo 0 > /sys/block/mmcblk3boot0/force_ro">Access boot partition</CMD>
  <CMD state="Updater" type="push" body="send" file="files/u-boot-imx6sx%board%_emmc.imx" ifdev="MX6SX">Sending u-boot.bin</CMD>
  <CMD state="Updater" type="push" body="$ dd if=$FILE of=/dev/mmcblk3boot0 bs=512 seek=2">Write u-boot into eMMC</CMD>
  <CMD state="Updater" type="push" body="$ echo 1 > /sys/block/mmcblk3boot0/force_ro"> Re-enable read-only </CMD>
  <CMD state="Updater" type="push" body="$ mmc bootpart enable 1 1 /dev/mmcblk3">Enable boot partion 1 to boot</CMD>

  <!-- Create fat partition -->
  <CMD state="Updater" type="push" body="$ while [ ! -e /dev/mmcblk3p1 ]; do sleep 1; echo \"waiting...\"; done ">Waiting for the partition ready</CMD>
  <CMD state="Updater" type="push" body="$ mkfs.vfat /dev/mmcblk3p1">Formatting rootfs partition</CMD>
  <CMD state="Updater" type="push" body="$ mkdir -p /mnt/mmcblk3p1"/>
  <CMD state="Updater" type="push" body="$ mount -t vfat /dev/mmcblk3p1 /mnt/mmcblk3p1"/>

  <!-- Burn zImage (Kernel) on p1 -->
  <CMD state="Updater" type="push" body="send" file="files/zImage">Sending kernel zImage</CMD>
  <CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk3p1/zImage">Write kernel image to eMMC card @ p1</CMD>

  <!-- Burn dtb on p1 -->
  <CMD state="Updater" type="push" body="send" file="files/zImage-imx6sx-%sxdtb%-emmc.dtb" ifdev="MX6SX">Sending Device Tree file</CMD>
  <CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk3p1/imx6sx-%sxdtb%.dtb" ifdev="MX6SX">Writing device tree file to sd card @ p1</CMD>
  <CMD state="Updater" type="push" body="$ umount /mnt/mmcblk3p1">Unmounting vfat partition</CMD>

  <!-- Populate default rootfs on p2 -->
  <CMD state="Updater" type="push" body="$ mkfs.ext3 -F -E nodiscard /dev/mmcblk3p2">Formatting rootfs partition</CMD>
  <CMD state="Updater" type="push" body="$ mkdir -p /mnt/mmcblk3p2"/>
  <CMD state="Updater" type="push" body="$ mount -t ext3 /dev/mmcblk3p2 /mnt/mmcblk3p2"/>
  <CMD state="Updater" type="push" body="pipe tar -jxv -C /mnt/mmcblk3p2" file="files/rootfs.tar.bz2" ifdev="MX6SL MX6D MX6Q MX6SX">Sending and Flashing rootfs partition to sd card @ p2</CMD>
  <CMD state="Updater" type="push" body="frf">Finishing rootfs image write on p2</CMD>
  <CMD state="Updater" type="push" body="$ umount /mnt/mmcblk3p2">Unmounting rootfs partition</CMD>

  <CMD state="Updater" type="push" body="$ echo Update Complete!">Done</CMD>
</LIST>

I included the XML file for you to see the similitude between the two files. This way you can adapt your XML file if you have one already. If not, you just copy paste the utp-flash-commands.sh and modify it according to your needs.

 

 

 

How to Flash the Board

In order to flash the board you should do the following:

user@linux-machine ~ $ cd fsl-release-bsp
user@linux-machine ~/fsl-release-bsp $ sudo ./board-flash ~/ utp-flash-commands.sh

 

 

This is it!!!

 

Following these instructions, you will be able to flash your board from a Linux machine without having to rely on the Freescale manufacturing tool that runs on windows. Please try these out and let me know in the comments if there is something to be changed, added or removed.

Outcomes