mirror of
https://github.com/lingble/meta-tegra.git
synced 2025-10-29 19:42:41 +00:00
tegra-flash-init: add recipe for supporting initrd flashing
* Booted via RCM boot process, running entirely within the initramfs * Accepts command sequence from host for handling specific flashing steps * Programs boot device (SPI flash or mmcblk0bootX devices) from signed binaries sent from host * Exports storage devices via USB gadget to host for partitioning/writing Signed-off-by: Matt Madison <matt@madison.systems>
This commit is contained in:
committed by
Matt Madison
parent
b112feca67
commit
5e23c6117b
210
recipes-core/initrdscripts/tegra-flash-init/init-flash.sh
Normal file
210
recipes-core/initrdscripts/tegra-flash-init/init-flash.sh
Normal file
@@ -0,0 +1,210 @@
|
||||
#!/bin/sh
|
||||
PATH=/sbin:/bin:/usr/sbin:/usr/bin
|
||||
mount -t proc proc -o nosuid,nodev,noexec /proc
|
||||
mount -t devtmpfs none -o nosuid /dev
|
||||
mount -t sysfs sysfs -o nosuid,nodev,noexec /sys
|
||||
mount -t configfs configfs -o nosuid,nodev,noexec /sys/kernel/config
|
||||
|
||||
[ ! /usr/sbin/wd_keepalive ] || /usr/sbin/wd_keepalive &
|
||||
|
||||
sernum=$(cat /sys/devices/platform/efuse-burn/ecid 2>/dev/null)
|
||||
[ -n "$sernum" ] || sernum=$(cat /sys/module/tegra_fuse/tegra_chip_uid 2>/dev/null)
|
||||
if [ -n "$sernum" ]; then
|
||||
# Restricted to 8 characters for the ID_MODEL tag
|
||||
sernum=$(printf "%x" "$sernum" | tail -c8)
|
||||
fi
|
||||
[ -n "$sernum" ] || sernum="UNKNOWN"
|
||||
UDC=$(ls -1 /sys/class/udc | head -n 1)
|
||||
|
||||
wait_for_storage() {
|
||||
local file_or_dev="$1"
|
||||
local message="Waiting for $file_or_dev..."
|
||||
local tries
|
||||
for tries in $(seq 1 15); do
|
||||
if [ -e "$file_or_dev" ]; then
|
||||
if [ "$message" = "." ]; then
|
||||
echo "[OK]"
|
||||
else
|
||||
echo "Found $file_or_dev"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
echo -n "$message"
|
||||
message="."
|
||||
sleep 1
|
||||
done
|
||||
if [ $tries -ge 15 ]; then
|
||||
echo "[FAIL]"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_usb_export() {
|
||||
local storage_export="$1"
|
||||
local export_name="$2"
|
||||
wait_for_storage "$storage_export" || return 1
|
||||
if [ -e /sys/kernel/config/usb_gadget/l4t ]; then
|
||||
gadget-vid-pid-remove 1d6b:104
|
||||
fi
|
||||
sed -e"s,@SERIALNUMBER@,$sernum," -e"s,@STORAGE_EXPORT@,$storage_export," /etc/initrd-flash/initrd-flash.scheme.in > /run/initrd-flash.scheme
|
||||
chmod 0644 /run/initrd-flash.scheme
|
||||
gadget-import l4t /run/initrd-flash.scheme
|
||||
printf "%-8s%-16s" "$export_name" "$sernum" > /sys/kernel/config/usb_gadget/l4t/functions/mass_storage.l4t_storage/lun.0/inquiry_string
|
||||
echo "$UDC" > /sys/kernel/config/usb_gadget/l4t/UDC
|
||||
if [ -e /sys/class/usb_role/usb2-0-role-switch/role ]; then
|
||||
echo "device" > /sys/class/usb_role/usb2-0-role-switch/role
|
||||
fi
|
||||
echo "Exported $storage_export as $export_name"
|
||||
return 0
|
||||
}
|
||||
|
||||
wait_for_connect() {
|
||||
local suspended
|
||||
local count=0
|
||||
echo -n "Waiting for host to connect..."
|
||||
while true; do
|
||||
suspended=$(expr $(cat /sys/class/udc/$UDC/device/gadget/suspended) \+ 0)
|
||||
if [ $suspended -eq 0 ]; then
|
||||
echo "[connected]"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
count=$(expr $count \+ 1)
|
||||
if [ $count -ge 5 ]; then
|
||||
echo -n "."
|
||||
count=0
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
wait_for_disconnect() {
|
||||
local suspended
|
||||
local count=0
|
||||
echo -n "Waiting for host to disconnect..."
|
||||
while true; do
|
||||
suspended=$(expr $(cat /sys/class/udc/$UDC/device/gadget/suspended) \+ 0)
|
||||
if [ $suspended -eq 1 ]; then
|
||||
echo "[disconnected]"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
count=$(expr $count \+ 1)
|
||||
if [ $count -ge 5 ]; then
|
||||
echo -n "."
|
||||
count=0
|
||||
fi
|
||||
done
|
||||
echo "" > /sys/kernel/config/usb_gadget/l4t/UDC
|
||||
return 0
|
||||
}
|
||||
|
||||
get_flash_package() {
|
||||
rm -rf /tmp/flashpkg_tree
|
||||
mkdir -p /tmp/flashpkg_tree/flashpkg/logs
|
||||
# Top of mount point on host will only be writable by root, so
|
||||
# create a world-writable subdirectory so a user can send us
|
||||
# the commands and content.
|
||||
chmod 777 /tmp/flashpkg_tree/flashpkg
|
||||
echo "PENDING: expecting command sequence from host" > /tmp/flashpkg_tree/flashpkg/status
|
||||
dd if=/dev/zero of=/tmp/flashpkg.ext4 bs=1M count=128 > /dev/null || return 1
|
||||
mke2fs -t ext4 -d /tmp/flashpkg_tree /tmp/flashpkg.ext4 > /dev/null || return 1
|
||||
setup_usb_export /tmp/flashpkg.ext4 flashpkg || return 1
|
||||
wait_for_connect || return 1
|
||||
wait_for_disconnect || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
process_bootloader_package() {
|
||||
rm -f /run/bootloader-status
|
||||
if program-boot-device /tmp/flashpkg/flashpkg/bootloader; then
|
||||
echo "SUCCESS" > /run/bootloader-status
|
||||
else
|
||||
echo "FAILED" > /run/bootloader-status
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
skip_status_report=
|
||||
reboot_type=
|
||||
final_status="FAILED"
|
||||
wait_for_bootloader=
|
||||
|
||||
if ! get_flash_package; then
|
||||
echo "Error retrieving flashing package" >&2
|
||||
skip_status_report=yes
|
||||
else
|
||||
mkdir -p /tmp/flashpkg
|
||||
mount -t ext4 /tmp/flashpkg.ext4 /tmp/flashpkg
|
||||
fi
|
||||
|
||||
if [ ! -e /tmp/flashpkg/flashpkg/conf/command_sequence ]; then
|
||||
echo "No command sequence in flash package, nothing to do"
|
||||
else
|
||||
exit_early=
|
||||
while read cmd args; do
|
||||
[ -z "$exit_early" ] || break
|
||||
echo "Processing: $cmd $args"
|
||||
case "$cmd" in
|
||||
bootloader)
|
||||
process_bootloader_package 2>&1 > /tmp/flashpkg/flashpkg/logs/bootloader.log &
|
||||
wait_for_bootloader=yes
|
||||
;;
|
||||
erase-mmc)
|
||||
if [ -b /dev/mmcblk0 ]; then
|
||||
blkdiscard -f /dev/mmcblk0 2>&1 > /tmp/flashpkg/flashpkg/logs/erase-mmc.log
|
||||
else
|
||||
echo "/dev/mmcblk0 does not exist, skipping" > /tmp/flashpkg/flashpkg/logs/erase-mmc.log
|
||||
fi
|
||||
;;
|
||||
export-devices)
|
||||
for dev in $args; do
|
||||
if setup_usb_export /dev/$dev $dev 2>&1 > /tmp/flashpkg/flashpkg/logs/export-$dev.log; then
|
||||
if wait_for_connect 2>&1 >> /tmp/flashpkg/flashpkg/logs/export-$dev.log; then
|
||||
if wait_for_disconnect 2>&1 >> /tmp/flashpkg/flashpkg/logs/export-$dev.log; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo "Export of $dev failed" >&2
|
||||
exit_early=yes
|
||||
break
|
||||
done
|
||||
;;
|
||||
reboot)
|
||||
reboot_type="$args"
|
||||
final_status="SUCCESS"
|
||||
# reboot command is expected to be the last in the sequence, if present
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "Unrecognized command: $cmd $args" > /tmp/flashpkg/flashpkg/logs/commandloop.log
|
||||
exit_early="yes"
|
||||
;;
|
||||
esac
|
||||
done < /tmp/flashpkg/flashpkg/conf/command_sequence
|
||||
fi
|
||||
|
||||
if [ -n "$wait_for_bootloader" ]; then
|
||||
message="Waiting for boot device programming to complete..."
|
||||
while [ ! -e /run/bootloader-status ]; do
|
||||
echo -n "$message"
|
||||
message="."
|
||||
sleep 1
|
||||
done
|
||||
blstatus=$(cat /run/bootloader-status)
|
||||
if [ "$blstatus" = "FAILED" ]; then
|
||||
final_status="FAILED"
|
||||
fi
|
||||
fi
|
||||
echo "$final_status" > /tmp/flashpkg/flashpkg/status
|
||||
if [ -z "$skip_status_report" ]; then
|
||||
umount /tmp/flashpkg && setup_usb_export /tmp/flashpkg.ext4 flashpkg && wait_for_connect && wait_for_disconnect
|
||||
fi
|
||||
|
||||
if [ "$reboot_type" = "forced-recovery" ]; then
|
||||
reboot-recovery
|
||||
else
|
||||
reboot -f
|
||||
fi
|
||||
@@ -0,0 +1,66 @@
|
||||
attrs :
|
||||
{
|
||||
bcdUSB = 0x210;
|
||||
bDeviceClass = 0x0;
|
||||
bDeviceSubClass = 0x0;
|
||||
bDeviceProtocol = 0x0;
|
||||
bMaxPacketSize0 = 0x40;
|
||||
idVendor = 0x1D6B;
|
||||
idProduct = 0x104;
|
||||
bcdDevice = 0x1;
|
||||
};
|
||||
os_descs :
|
||||
{
|
||||
config_id = 1;
|
||||
use = 1;
|
||||
qw_sign = "MSFT100";
|
||||
b_vendor_code = 0xcd;
|
||||
};
|
||||
strings = (
|
||||
{
|
||||
lang = 0x409;
|
||||
manufacturer = "NVIDIA";
|
||||
product = "Linux for Tegra";
|
||||
serialnumber = "@SERIALNUMBER@";
|
||||
} );
|
||||
functions :
|
||||
{
|
||||
mass_storage_l4t :
|
||||
{
|
||||
instance = "l4t_storage";
|
||||
type = "mass_storage";
|
||||
attrs :
|
||||
{
|
||||
stall = true;
|
||||
luns = (
|
||||
{
|
||||
cdrom = false;
|
||||
ro = false;
|
||||
nofua = false;
|
||||
removable = true;
|
||||
file = "@STORAGE_EXPORT@";
|
||||
});
|
||||
};
|
||||
os_descs = ( );
|
||||
};
|
||||
};
|
||||
configs = (
|
||||
{
|
||||
id = 1;
|
||||
name = "L4T";
|
||||
attrs :
|
||||
{
|
||||
bmAttributes = 0x80;
|
||||
bMaxPower = 0x2;
|
||||
};
|
||||
strings = (
|
||||
{
|
||||
lang = 0x409;
|
||||
configuration = "UMS";
|
||||
} );
|
||||
functions = (
|
||||
{
|
||||
name = "ums.0";
|
||||
function = "mass_storage_l4t";
|
||||
} );
|
||||
} );
|
||||
@@ -0,0 +1,147 @@
|
||||
#!/bin/sh
|
||||
|
||||
BOOTPART_SIZE=
|
||||
BOOTDEV_TYPE=unset
|
||||
|
||||
program_spi_partition() {
|
||||
local partname="$1"
|
||||
local part_offset="$2"
|
||||
local part_size="$3"
|
||||
local part_file="$4"
|
||||
local file_size=0
|
||||
|
||||
if [ -n "$part_file" ]; then
|
||||
file_size=$(stat -c "%s" "$part_file")
|
||||
if [ -z "$file_size" ]; then
|
||||
echo "ERR: could not retrieve file size of $part_file" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if [ $file_size -ne 0 ]; then
|
||||
echo "Writing $part_file (size=$file_size) to $partname (offset=$part_offset)"
|
||||
if ! mtd_debug write /dev/mtd0 $part_offset $file_size "$part_file"; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
# Multiple copies of the BCT get installed at erase-block boundaries
|
||||
# within the defined BCT partition
|
||||
if [ "$partname" = "BCT" ]; then
|
||||
local slotsize=$(cat /sys/class/mtd/mtd0/erasesize)
|
||||
if [ -z "$slotsize" ]; then
|
||||
return 1
|
||||
fi
|
||||
local rounded_slot_size=$(expr \( \( $slotsize \+ 511 \) / 512 \) \* 512)
|
||||
local curr_offset=$(expr $part_offset \+ $rounded_slot_size)
|
||||
local copycount=$(expr $part_size / $rounded_slot_size)
|
||||
local i=1
|
||||
while [ $i -lt $copycount ]; do
|
||||
echo "Writing $part_file to BCT+$i (offset=$curr_offset)"
|
||||
if ! mtd_debug write /dev/mtd0 $curr_offset $file_size "$part_file"; then
|
||||
return 1
|
||||
fi
|
||||
i=$(expr $i \+ 1)
|
||||
curr_offset=$(expr $curr_offset \+ $rounded_slot_size)
|
||||
done
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
program_mmcboot_partition() {
|
||||
local partname="$1"
|
||||
local part_offset="$2"
|
||||
local part_size="$3"
|
||||
local part_file="$4"
|
||||
local file_size=0
|
||||
local bootpart="/dev/mmcblk0boot0"
|
||||
|
||||
if [ -z "$BOOTPART_SIZE" ]; then
|
||||
echo "ERR: boot partition size not set" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ $part_offset -ge $BOOTPART_SIZE ]; then
|
||||
part_offset=$(expr $part_offset - $BOOTPART_SIZE)
|
||||
bootpart="/dev/mmcblk0boot1"
|
||||
fi
|
||||
if [ -n "$part_file" ]; then
|
||||
file_size=$(stat -c "%s" "$part_file")
|
||||
if [ -z "$file_size" ]; then
|
||||
echo "ERR: could not retrieve file size of $part_file" >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
if [ $file_size -ne 0 ]; then
|
||||
echo "Writing $part_file (size=$file_size) to $partname on $bootpart (offset=$part_offset)"
|
||||
if ! dd if="$part_file" of="$bootpart" bs=1 seek=$part_offset count=$file_size > /dev/null; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
program_boot_partitions() {
|
||||
local blpkgdir="$1"
|
||||
local partname part_offset part_size part_file
|
||||
local rc=0
|
||||
|
||||
if [ -z "$blpkgdir" -o ! -d "$blpkgdir" ]; then
|
||||
echo "ERR: missing or non-directory: $blpkgdir" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ -f "$blpkgdir/boot_device_type" ]; then
|
||||
BOOTDEV_TYPE=$(cat "$blpkgdir/boot_device_type")
|
||||
else
|
||||
echo "ERR: missing boot_device_type file in $blpkgdir" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ ! -f "$blpkgdir/partitions.conf" ]; then
|
||||
echo "ERR: partition config missing in $blpkgdir" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$BOOTDEV_TYPE" = "mmcboot" ]; then
|
||||
if [ ! -b /dev/mmcblk0boot0 -o ! -b /dev/mmcblk0boot1 ]; then
|
||||
echo "ERR: eMMC boot device, but mmcblk0bootX devices do not exist" >&2
|
||||
return 1
|
||||
fi
|
||||
BOOTPART_SIZE=$(expr $(cat /sys/block/mmcblk0boot0/size) \* 512)
|
||||
echo "0" > /sys/block/mmcblk0boot0/force_ro
|
||||
echo "0" > /sys/block/mmcblk0boot1/force_ro
|
||||
blkdiscard -f /dev/mmcblk0boot0
|
||||
blkdiscard -f /dev/mmcblk0boot1
|
||||
elif [ "$BOOTDEV_TYPE" = "spi" ]; then
|
||||
if [ ! -e /dev/mtd0 ]; then
|
||||
echo "ERR: SPI boot device, but mtd0 device does not exist" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Erasing /dev/mtd0"
|
||||
flash_erase /dev/mtd0 0 0
|
||||
else
|
||||
echo "ERR: unknown boot device type: $BOOTDEV_TYPE" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
oldwd="$PWD"
|
||||
cd "$blpkgdir"
|
||||
|
||||
while IFS=":" read partname part_offset part_size part_file; do
|
||||
if [ "$BOOTDEV_TYPE" = "spi" ]; then
|
||||
if ! program_spi_partition "$partname" "$part_offset" "$part_size" "$part_file"; then
|
||||
rc=1
|
||||
break
|
||||
fi
|
||||
elif [ "$BOOTDEV_TYPE" = "mmcboot" ]; then
|
||||
if ! program_mmcboot_partition "$partname" "$part_offset" "$part_size" "$part_file"; then
|
||||
rc=1
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done < partitions.conf
|
||||
if [ "$BOOTDEV_TYPE" = "mmcboot" ]; then
|
||||
echo "1" > /sys/block/mmcblk0boot0/force_ro
|
||||
echo "1" > /sys/block/mmcblk0boot1/force_ro
|
||||
fi
|
||||
cd "$oldwd"
|
||||
return $rc
|
||||
}
|
||||
|
||||
program_boot_partitions "$@"
|
||||
29
recipes-core/initrdscripts/tegra-flash-init_1.0.bb
Normal file
29
recipes-core/initrdscripts/tegra-flash-init_1.0.bb
Normal file
@@ -0,0 +1,29 @@
|
||||
DESCRIPTION = "Minimal initramfs init script for initrd flashing"
|
||||
LICENSE = "MIT"
|
||||
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
|
||||
|
||||
SRC_URI = "\
|
||||
file://init-flash.sh \
|
||||
file://program-boot-device.sh \
|
||||
file://initrd-flash.scheme.in \
|
||||
"
|
||||
|
||||
COMPATIBLE_MACHINE = "(tegra)"
|
||||
|
||||
S = "${WORKDIR}"
|
||||
|
||||
do_install() {
|
||||
install -m 0755 ${WORKDIR}/init-flash.sh ${D}/init
|
||||
install -m 0555 -d ${D}/proc ${D}/sys
|
||||
install -m 0755 -d ${D}/dev ${D}/mnt ${D}/run ${D}/usr
|
||||
install -m 1777 -d ${D}/tmp
|
||||
mknod -m 622 ${D}/dev/console c 5 1
|
||||
install -d ${D}${bindir}
|
||||
install -m 0755 ${WORKDIR}/program-boot-device.sh ${D}${bindir}/program-boot-device
|
||||
install -d ${D}${sysconfdir}/initrd-flash
|
||||
install -m 0644 ${WORKDIR}/initrd-flash.scheme.in ${D}${sysconfdir}/initrd-flash/
|
||||
}
|
||||
|
||||
FILES:${PN} = "/"
|
||||
RDEPENDS:${PN} = "util-linux-blkdiscard tegra-flash-reboot mtd-utils e2fsprogs-mke2fs libusbgx-tegra-initrd-flash watchdog-keepalive"
|
||||
RRECOMMENDS:${PN} = "kernel-module-spi-tegra114 kernel-module-loop"
|
||||
Reference in New Issue
Block a user