Skip to content

Instantly share code, notes, and snippets.

@foxbunny
Created May 16, 2016 13:53
Show Gist options
  • Save foxbunny/65c145d91dcbfa807a2da0f58b1eba8f to your computer and use it in GitHub Desktop.
Save foxbunny/65c145d91dcbfa807a2da0f58b1eba8f to your computer and use it in GitHub Desktop.
Script to flash CHIP
#!/bin/bash
#
# This script programs the CHIP's NAND flash using sunxi-tools' `fel` utiltiy,
# and U-Boot itself. The following tools must be present on the system:
#
# - dd (coreutils)
# - lsusb (usbutils)
# - fel (sunxi-tools)
# - mkimage (android-tools)
#
# The end result is the following flash layout:
#
# ======== ======== ============ ====================================
# mtdpart size MB name description
# -------- -------- ------------ ------------------------------------
# 0 4 spl Master SPL binary
# 1 4 spl-backup Backup SPL binary
# 3 4 uboot U-Boot binary
# 4 4 env U-Boot environment
# 5 8 linux Linux kernel zImage w/DTB
# 6 64 root Root filesystem ubi volume
# 7 64 root-backup Backup root filesystem ubi volume
# 8 32 overlay Root filesytem overlay ubi volume
# 9 600 cache Download
# 10 - data Application data
# ======== ======== ============ ====================================
#
# Of the partitions in the table, 0 through 7 are flashed by this tool. The
# others are assumed to be created in the userspace during boot.
#
# The flashing works roughly like this:
#
# CHIP is put into FEL mode by jumping the FEL pin (3rd on U14L) to ground
# (first or last on U14L) during power-on. This allows the user to execute
# arbitrary bare-metal code on the Allwinner CPU. The `fel` tool is used to
# execute the SPL generated by the build (sunxi-spl.bin) and is then used to
# transfer the prepared files to DRAM on the board. One of the files that are
# transferred to the board's memory is a specially crafted U-Boot script that
# performs the transfer of the payload from memory to NAND flash. After all
# payloads are transferred to DRAM, the U-Boot binary in the memory is
# executed, which in turns runs the prepared U-Boot script.
#
# This file is part of rxOS.
# rxOS is free sofware licensed under the
# GNU GPL version 3 or any later version.
#
# (c) 2016 Outernet Inc
# Some rights reserved.
set -e
# Set-up
SCRIPTDIR="$(dirname "$0")"
. "$SCRIPTDIR/helpers.sh"
# Relevant paths
RXOS_DIR="$(cd "$SCRIPTDIR/../../"; pwd)"
HOST_DIR="$RXOS_DIR/out/host"
BINARIES_DIR="$RXOS_DIR/out/images"
TMPDIR=
# Execution parameters
TMPDIR_TEMPLATE="/tmp/chip-flash-XXXXXXX"
NAND_SIZE=0x100000000 # 4GB
CHIP_DEVID="0525:a4a7"
PAGE_SIZE=16384
PAGE_SIZE_HEX=0x4000
OOB_SIZE=1664
# Source files
SPL="$BINARIES_DIR/sunxi-spl.bin"
UBOOT="$BINARIES_DIR/u-boot-dtb.bin"
UBOOT_ENV="$BINARIES_DIR/uboot-env.bin"
LINUX="$BINARIES_DIR/zImage.sun5i-r8-chip"
ROOTFS="$BINARIES_DIR/rootfs.ubifs.lzo"
# Memory locations
SPL_ADDR=0x43000000
UBOOT_SCRIPT_ADDR=0x43100000
UBOOT_ADDR=0x4a000000
UBOOT_ENV_ADDR=0x4b000000
LINUX_ADDR=0x4c000000
ROOTFS_ADDR=0x4d000000
# Offsets
SPL_OFF=0x0 # 0 +0
SPL_BACKUP_OFF=0x40000 # 4M +4M
UBOOT_OFF=0x80000 # 8M +4M
UBOOT_ENV_OFF=0xC0000 # 12M +4M
LINUX_OFF=0x1000000 # 16M +4M
ROOTFS_OFF=0x1800000 # 24M +8M
ROOTFS_BACKUP_OFF=0x5800000 # 88M +64M
# Set path so Buildroot host utils can be reused
PATH="$HOST_DIR/bin":$PATH
# Abort with an error message
abort() {
local msg="$*"
echo "ERROR: $msg"
exit 1
}
# Print usage
usage() {
echo "Usage: $0 [-htkD]"
echo
echo "Options:"
echo " -h Show this message and exit"
echo " -t Test mode: do not boot into the flashed system "
echo " (remain in U-boot)"
echo " -k Keep temporary directory"
echo " -D Dry run (do not perform any flashing, just echo"
echo " what the script would do)"
echo
echo "This program is part of rxOS."
echo "rxOS is free software licensed under the"
echo "GNU GPL version 3 or any later version."
echo
echo "(c) 2016 Outernet Inc"
echo "Some rights reserved."
echo
}
# Echo a command if in DRY_MODE, otherwise run it
#
# The command passed to this function must be a single string. It is executed
# using the `eval` command.
do_or_dry() {
local command="$1"
if [ "$DRY_RUN" == y ]; then
command="${command//\|/\\|}"
command="${command//\"/\\\"}"
eval "echo DRY: ${command}"
else
eval "$command"
fi
}
# Check whether a command exists
has_command() {
local command="$1"
which "$command" > /dev/null 2>&1
}
# Check whether a device with specific device ID exists
has_dev() {
local devid="$1"
lsusb | grep -q "$devid" > /dev/null 2>&1
}
# Check that the specified path exists and abort if it does not.
check_file() {
local path="$1"
[ -f "$path" ] || abort "File not found: '$path'
Is the build finished?"
}
# Keep executing a specified command until it succeeds or times out
#
# Arguments:
#
# timeout: total pauses
# pause: pause duration in seconds
# message: message shown before the command executes
# command: command that checks status
#
# The command must be quoted.
with_timeout() {
local timeout="$1"
local pause="$2"
local msg="$3"
local cmd="$4"
echo -n "${msg}..."
for i in {1..$timeout}; do
$cmd > /dev/null 2>&1 && echo "OK" && return 0
sleep "$pause"
echo -n "."
done
echo "TIMEOUT"
return 1
}
# Wait for CHIP in FEL mode to connected
wait_for_fel() {
with_timeout 60 1 "Waiting for CHIP in FEL mode" "fel ver"
}
# Wait for CHIP to reconnect after being flashed
wait_for_boot() {
with_timeout 30 6 "Flashing CHIP" "has_device $CHIP_DEVID"
}
# Print a number in hex format
hex() {
local num="$1"
printf "0x%08x" "$num"
}
# Return the size of a file in bytes
filesize() {
local path="$1"
stat --printf="%s" "$path"
}
# Return the size of a file in hex
hexsize() {
local path="$1"
hex "$(filesize "$path")"
}
# Return the size of a file in pages
pagesize() {
local path="$1"
local fsize
fsize="$(filesize "$path")"
hex "$((fsize / PAGE_SIZE))"
}
# Return the size of a padded SPL file with EEC in hex
splsize() {
local path="$1"
local fsize
fsize="$(filesize "$path")"
hex "$((fsize / PAGE_SIZE + OOB_SIZE))"
}
# Align a file to page boundary
#
# Arguments:
#
# in: input file path
# out: output file path
page_align() {
local in="$1"
local out="$2"
dd if="$in" of="$out" bs=$PAGE_SIZE conv=sync status=none
}
# Pad a file to specified size
#
# Arguments:
#
# size: target size (in hex notiation)
# path: path of the file to pad
#
# This function modifies the original file by appending the padding. Padding is
# a stream of random bytes sources from /dev/urandom.
#
# It is the caller's responsibility to ensure that the target size is larger
# than the current size.
pad_to() {
local size="$1"
local path="$2"
local source_size
local source_size_hex
local dpages
source_size="$(filesize "$path")"
source_size_hex="$(hexsize "$path")"
dpages="$(( (size - source_size_hex) / PAGE_SIZE_HEX ))"
dd if=/dev/urandom of="$path" seek="$source_size" bs=$PAGE_SIZE \
count="$dpages" status=none
}
# Create padded SPL with EEC (error correction code)
#
# Arguments:
#
# in: path to the source SPL binary
# out: output path
#
# This is a thin wrapper around `spl-image-builder` too provided by NTC. The
# arguments are as follows:
#
# -d disable scrambler
# -r repeat count
# -u usable page size
# -o OOB size
# -p page size
# -c ECC step size
# -s ECC strength
add_ecc() {
local in="$1"
local out="$2"
spl-image-builder -d -r 3 -u 4096 -o "$OOB_SIZE" -o "$PAGE_SIZE" -c 1024 \
-s 64 "$in" "$out"
}
###############################################################################
# SHOW STARTS HERE
###############################################################################
TEST_MODE=n
KEEP_TMPDIR=n
DRY_RUN=n
# Parse the command line options
while getopts "htkD" opt; do
case $opt in
h)
usage
exit 0
;;
t)
TEST_MODE=y
;;
k)
KEEP_TMPDIR=y
;;
D)
DRY_RUN=y
;;
\?)
echo "Invalid option: -$OPTARG" >&2
echo
usage
exit 1
;;
esac
done
# Check prereqisites
has_command fel || abort "Missing command 'fel'
Please install https://github.com/NextThingCo/sunxi-tools"
has_command spl-image-builder || abort "Missing command 'spl-image-builder'
Please install from https://github.com/NextThingCo/CHIP-tools @210f269"
has_command mkimage || abort "Missing command 'mkimage'
Please install android-tools"
has_command dd || abort "Missing command 'dd'
Please install coreutils"
has_command lsusb || abort "Missing command 'lsusb'
Please install usbutils"
# Check that sources exist
check_file "$SPL"
check_file "$UBOOT"
check_file "$UBOOT_ENV"
check_file "$LINUX"
check_file "$ROOTFS"
# Create and set tempdir
TMPDIR="$(mktemp -d "$TMPDIR_TEMPLATE")"
###############################################################################
# Prepare files
###############################################################################
msg "Preparing the payloads"
submsg "Preparing the SPL binary"
add_ecc "$SPL" "$TMPDIR/spl-with-ecc.bin"
SPL_SIZE=$(splsize "$TMPDIR/spl-with-ecc.bin")
submsg "Preparing the U-Boot binary"
page_align "$UBOOT" "$TMPDIR/uboot.bin"
UBOOT_SIZE=0xC0000
pad_to "$UBOOT_SIZE" "$TMPDIR/uboot.bin"
submsg "Preparing the U-Boot env file"
UBOOT_ENV_SIZE=0x40000
submsg "Preparing the kernel image"
page_align "$LINUX" "$TMPDIR/zImage"
LINUX_SIZE="$(hexsize "$TMPDIR/zImage")"
submsg "Preparing the rootfs image"
ROOTFS_SIZE="$(hexsize "$ROOTFS")"
###############################################################################
# Create script
###############################################################################
msg "Creating U-Boot script"
if [ "$TEST_MODE" == y ]; then
EXIT_MSG="FINISHED_FLASHING"
EXIT_CMD="whle true; do; sleep 10; done;"
else
EXIT_MSG="BOOTING"
EXIT_CMD="boot"
fi
submsg "Writing script source"
cat <<EOF > "$TMPDIR/uboot.cmds"
nand erase 0x0 $NAND_SIZE
sunxi_nand config spl
nand write.raw ${SPL_ADDR} ${SPL_OFF} ${SPL_SIZE}
nand write.raw ${SPL_ADDR} ${SPL_BACKUP_OFF} ${SPL_SIZE}
sunxi_nand config default
nand write ${UBOOT_ADDR} ${UBOOT_OFF} ${UBOOT_SIZE}
nand write ${UBOOT_ENV_ADDR} ${UBOOT_ENV_OFF} ${UBOOT_ENV_SIZE}
nand write ${LINUX_ADDR} ${LINUX_OFF} ${LINUX_SIZE}
nand slc-mode on
nand write.trimfs ${ROOTFS_ADDR} ${ROOTFS_OFF} ${ROOTFS_SIZE}
nand write.trimfs ${ROOTFS_ADDR} ${ROOTFS_BACKUP_OFF} ${ROOTFS_SIZE}
setenv bootargs earlyprintk
setenv bootcmd source \${scriptaddr}; nand slc-mode on; mtdparts; nand read \$kernel_addr_r linux; bootm \$kernel_addr_r
saveenv
mw \${scriptaddr} 0x0
echo
echo =================={{{ $EXIT_MSG }}}==================
echo
$EXIT_CMD
EOF
submsg "Writing script image"
mkimage -A arm -T script -C none -n "flash CHIP" -d "$TMPDIR/uboot.cmds" \
"$TMPDIR/uboot.scr" > /dev/null
###############################################################################
# Uploading
###############################################################################
msg "Uploading payloads"
# Wait for chip in FEL mode to become available
wait_for_fel || abort "Unable to find CHIP in FEL mode"
submsg "Executing SPL"
do_or_dry 'fel spl "$SPL"'
submsg "Uploading SPL"
do_or_dry 'fel write "$SPL_ADDR" "$TMPDIR/spl-with-ecc.bin"'
submsg "Uploading U-Boot"
do_or_dry 'fel write "$UBOOT_ADDR" "$TMPDIR/uboot.bin"'
submsg "Uploading U-Boot env"
do_or_dry 'fel write "$UBOOT_ENV_ADDR" "$UBOOT_ENV"'
submsg "Uploading Linux kernel"
do_or_dry 'fel write "$LINUX_ADDR" "$TMPDIR/zImage"'
submsg "Uploading rootfs image"
do_or_dry 'fel write "$ROOTFS_ADDR" "$ROOTFS"'
###############################################################################
# Executing flash
###############################################################################
msg "Executing flash"
do_or_dry 'fel exe "$UBOOT_ADDR"'
# Finish up
msg "Cleaning up"
[ "$KEEP_TMPDIR" == n ] && rm -rf "$TMPDIR"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment