Files
a8a9a8502d Dual storage options eMMC & TF Card (#63)
* feat: support dual storage variants (eMMC + SDMMC/TF card) and upgrade jetkvm_app to 0.5.6

Introduce two separate build configurations for JetKVM V2 hardware
variants that differ only in storage medium:

  BoardConfig-EMMC-NONE-RV1106_JETKVM_V2.mk  → boots from eMMC  (mmcblk0, 13640M userdata)
  BoardConfig-SDMMC-NONE-RV1106_JETKVM_V2.mk → boots from TF card (mmcblk1, dynamic userdata)

DTS refactor:
- Extract all shared board peripherals (backlight, gmac, i2c1/AT24C02,
  i2c3/FT5x06, spi0/ST7789V, uart3, rtc, wdt, pwm5, sfc=disabled) into
  rv1106-jetkvm-v2.dtsi as a common base
- rv1106g-jetkvm-v2.dts becomes a thin file with only the eMMC node
- New rv1106g-jetkvm-v2-sdmmc.dts thin file with only the sdmmc node
- Verified: both DTBs compile correctly; eMMC DTB is binary-identical
  to upstream

Build system:
- Add sdmmc as a new RK_BOOT_MEDIUM type throughout project/build.sh:
  sd_parts device, parse_partition_file, rootfs bootarg, build_recovery,
  build_factory, build_uboot INI selection, and mount_part script
- sysdrv/Makefile: add sdmmc block for KERNEL_CMDLINE_FRAGMENT and env
  target using mmcblk1; restore emmc back to mmcblk0
- project/scripts/mk-tftp_sd_update.sh: recognize sdmmc alongside emmc
- tools/linux/Linux_Pack_Firmware/mk-update_pack.sh: add mmcblk0 to the
  boot medium list so eMMC update packages are correctly generated

U-Boot:
- android_ab.c: replace hardcoded mmcblk1 with dev_desc->devnum so the
  A/B root device path is correct for both eMMC (devnum=0) and SDMMC
  (devnum=1), matching the DTS alias assignments

jetkvm_app:
- Upgrade from 0.4.8 to 0.5.6 (SHA256 verified)
- Note: 0.5.6 switches from static to dynamic linking (ld-uClibc.so.0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: switch to ubuntu-22.04 runner and build both EMMC and SDMMC variants

- Replace buildjet-16vcpu-ubuntu-2204 with standard ubuntu-22.04 runner
- Use matrix strategy (fail-fast: false) to build EMMC and SDMMC in parallel
- Artifact names include board variant: OTA-Image-EMMC, Full-Image-EMMC, etc.
- buildkit upload remains EMMC-only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: bake SKU identifier into system image for OTA targeting

Each build variant writes /etc/jetkvm-sku to the system partition so
the jetkvm_app can report the correct SKU when querying the OTA service:

  BoardConfig-EMMC  → RK_SKU=jetkvm-emmc
  BoardConfig-SDMMC → RK_SKU=jetkvm-sdmmc

Matches the SKU routing introduced in jetkvm/cloud-api#49, where the
OTA API serves variant-specific binaries under
{prefix}/{version}/skus/{sku}/ and defaults to legacy path for
unrecognised SKUs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: align OTA SKU names with cloud-api DEFAULT_SKU convention

  jetkvm-emmc  → jetkvm-v2       (matches cloud-api DEFAULT_SKU)
  jetkvm-sdmmc → jetkvm-v2-sdmmc

Ref: jetkvm/cloud-api@1958830

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(dts): remove contradictory cap-mmc-highspeed from sdmmc node

no-mmc prevents MMC protocol probing entirely, making cap-mmc-highspeed
dead and misleading. Only cap-sd-highspeed is relevant for TF card use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: handle IGNORE mountpoint for sdmmc in mount_part script

Line 1601's guard only matched emmc, so SDMMC partitions with
IGNORE mountpoint (e.g. inactive A/B slot) would fall through to
return 1 instead of return 0, causing boot-time mount errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: add sdmmc to block-device filesystem tool checks in build_mkimg

erofs and squashfs paths were missing sdmmc, causing them to fall
through to ubifs (NAND-only tool). Latent with current ext4-only
config but would silently produce a corrupt image if fs type changed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(sdmmc): build dd-able SD card image for direct flashing

Adds build_sd_dd_image() that assembles a flat raw image from the
fixed-size partitions (env, idblock, uboot_a/b, boot_a/b, system_a/b)
plus a 256 MiB seed userdata.img containing install_to_userdata
content (jetkvm_app etc.). Layout follows the cmdline blkdevparts —
no MBR/GPT, since the kernel reads partitioning from u-boot env.

Why a seed userdata: the SDMMC partition spec terminates with
'-(userdata)' which build_mkimg skips, so jetkvm_app never landed in
update.img on SDMMC. The mount_part init script (project/build.sh
mount_part) calls resize2fs on first boot, which grows the 256 MiB
ext4 to the end of the SD card automatically.

Workflow: SDMMC artifact is now update_sd.img.zip (zip -9 of a sparse
~1.4 GiB image, ~45 MiB compressed). update.img remains the EMMC
artifact since RKDevTool still uses it for production flashing.

* fix(sdmmc): chown userdata seed root:root before mkfs.ext4

build_mkimg userdata is a no-op on SDMMC (partition size '-' → 0), so the
__RELEASE_FILESYSTEM_FILES chown that build_mkimg normally runs never fires.
Without it, mkfs.ext4 -d preserves the builder's uid/gid into the image, so
/userdata/jetkvm on the SD card depended on whoever ran the build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(sdmmc): tune userdata mkfs.ext4 so first-boot resize2fs scales to 1 TiB

The previous flags (mkfs default 1K block size, -O ^64bit,^huge_file) made
the first-boot resize hang on large cards:

  - 1K blocks × 8K-blocks-per-group → 128K block groups for 1 TiB. resize2fs
    has to write GD/bitmap entries for every new group; on slow SD random
    writes this stretches to many minutes.
  - Default inode_ratio at 1K = 2048 inodes/group → ~256M inodes after
    resize. ext4lazyinit kthread then zeroes ~32 GiB of inode tables in
    the background, saturating SD I/O long after mount returns.
  - resize_inode reserve = 256 GDT blocks × 32 entries = 8192 group
    descriptors, capping resize_inode-style growth at 64 GiB. Beyond that,
    resize2fs falls back to meta_bg, but ^64bit blocks the cleaner path.

Switch to: 4K blocks, -T largefile (1 inode/MiB), -O 64bit, and an explicit
-E resize= reservation covering 2 TiB. Measured on a 1 TiB sparse loop
(host mkfs.ext4 1.47.2): online resize drops from 40 s to 0.4 s, and the
post-mount lazy-itable workload drops from ~32 GiB to ~256 MiB of writes.

32 GiB minimum supported card capacity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(sdmmc): include sdmmc in sysdrv build-medium switches

Three places in sysdrv/ branched on the boot medium but left sdmmc out:

  - Makefile.tools.board.mk: e2fsprogs was gated on BOOT_MEDIUM=emmc, so
    the SDMMC rootfs shipped without resize2fs/e2fsck/mke2fs. mount_part's
    first-boot resize2fs call then silently failed and userdata stayed
    locked at the 256 MiB seed size, never growing to fill the card.

  - Makefile.param: the RK_BOOT_MEDIUM whitelist that propagates BOOT_MEDIUM
    omitted sdmmc, so any sub-tool Makefile included Makefile.param
    standalone (dropbear, eudev, etc.) saw BOOT_MEDIUM unset for sdmmc.
    Currently benign because no sub-tool branches on BOOT_MEDIUM, but a
    latent footgun.

  - tools/pc/toolkits/get_part_info.sh: PARTITION_FIRST_NO=0 was applied
    only for emmc. mmcblk1pN is also 1-indexed, so sdmmc would produce
    off-by-one root=/dev/mmcblk1pN values via `make env -C sysdrv`. The
    project's build.sh path computes part_num itself and isn't affected,
    but the sysdrv path was wrong.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 23:55:26 +02:00

249 lines
5.1 KiB
Bash
Executable File

#!/bin/bash
cmd=`realpath $0`
cwd=`dirname $cmd`
CMD_VERSION=1.0.1
C_BLACK="\e[30;1m"
C_RED="\e[31;1m"
C_GREEN="\e[32;1m"
C_YELLOW="\e[33;1m"
C_BLUE="\e[34;1m"
C_PURPLE="\e[35;1m"
C_CYAN="\e[36;1m"
C_WHITE="\e[37;1m"
C_NORMAL="\033[0m"
function msg_info()
{
echo -e "${C_GREEN}[$(basename $0):info] $1${C_NORMAL}"
}
function msg_warn()
{
echo -e "${C_YELLOW}[$(basename $0):warn] $1${C_NORMAL}"
}
function msg_error()
{
echo -e "${C_RED}[$(basename $0):error] $1${C_NORMAL}"
}
function pause()
{
msg_error "package update.img fail..."
msg_error "Press any key to quit:"
read -n1 -t10 -s key
exit -1
}
function msg_help()
{
echo "command format:"
echo " `basename $cmd` -v -h -i -id -p -t"
echo " -v|--version (Optional) view this version."
echo " -h|--help (Optional) view this help message."
echo " -id|--chip-id (Essential) input chip id."
echo " -i|--image (Essential) input firmware image path."
echo " -p|--parameter (Optional) the partition file path (Not added by default)."
}
function chk_param()
{
local cnt
cnt=0
while [ $# -ne 0 ]
do
case $1 in
-v|--version)
msg_info "V$CMD_VERSION"
exit 0
;;
-h|--help)
msg_help
exit 0
;;
-i|--image)
IMAGE_PATH=$2
cnt=$(( cnt + 1 ))
;;
-id|--chip-id)
CHIP_ID=$2
case $CHIP_ID in
rv1126|rv1109|RV1126|RV1109)
CHIP_ID=-RK1126
;;
rk3588|RK3588)
CHIP_ID=-RK3588
;;
rv1106|rv1103|RV1106|RV1103)
CHIP_ID=-RK1106
;;
*)
msg_error "Invalid paramter: -id $CHIP_ID"
exit -1
;;
esac
cnt=$(( cnt + 1 ))
;;
-p|--parameter)
partition_file=$2
;;
*)
;;
esac
shift
done
if [ $cnt -ne 2 -o -z "$IMAGE_PATH" -o -z "$CHIP_ID" ]; then
msg_error "Invalid parameter..."
msg_help
exit -1
fi
msg_info "image input dir: $IMAGE_PATH"
if [ -f "$partition_file" ]; then
PARTITION_FILE=`basename $partition_file`
return
fi
PARTITION_FILE=$partition_file
if [ -f "$IMAGE_PATH/$PARTITION_FILE" ]; then
return
fi
if [ -f "$IMAGE_PATH/env.img" ]; then
PARTITION_FILE=env.img
return
else
msg_error "Not found file named $PARTITION_FILE in the dir $IMAGE_PATH"
exit -1
fi
}
function get_partition_names()
{
local part_name part_size
local part_file part_lines boot_mediums
local env_all_flag env_final env_final_offset
env_all_flag=0
part_file=$IMAGE_PATH/$PARTITION_FILE
boot_mediums="blkdevparts=mmcblk0,blkdevparts=mmcblk1,mtdparts=sfc_nor,mtdparts=spi-nand0,mtdparts=rk-nand"
IFS=,
for medium in $boot_mediums;
do
part_lines=$(strings $part_file | grep -i $medium)
[ ! -z "$part_lines" ] && part_lines=${part_lines##*${medium}:} && break
done
IFS=
[ -z "$part_lines" ] && msg_error "Not found partition line in $part_file" && msg_help && exit -1
IFS=,
read -ra env_arr <<< "$part_lines"
env_final=${env_arr[-1]}
if [[ $env_final =~ "@" ]]; then
env_final_offset=`echo $env_final | cut -s -d '(' -f1|cut -s -d '@' -f2`
if [[ $((env_final_offset)) == 0 ]]; then
env_all_flag=1
fi
fi
for part in $part_lines;
do
part_size=`echo $part | cut -d '@' -f1`
part_name=`echo $part | cut -d '(' -f2|cut -d ')' -f1`
[[ $part_size =~ "-" ]] && continue
[[ "$part" == "$env_final" && "$env_all_flag" == 1 ]] && continue
PARTITIONS="$part_name,$PARTITIONS"
done
IFS=
if [ -z "$PARTITIONS" ];then
msg_error "Invalid partition..."
exit -1
fi
if [[ "$env_all_flag" == 1 ]]; then
ENV_ALL_OPTS="-no-overlap-check"
fi
# msg_info "partitions:\n$PARTITIONS"
}
function __package_file()
{
if [ -f "$IMAGE_PATH/$1" ];then
PACKAGE_CFG="$part $1\n$PACKAGE_CFG"
elif [ -f "$IMAGE_PATH/${1}.img" ];then
PACKAGE_CFG="$part ${1}.img\n$PACKAGE_CFG"
elif [ -f "$IMAGE_PATH/${1%_[ab]}.img" ];then
PACKAGE_CFG="$part ${1%_[ab]}.img\n$PACKAGE_CFG"
else
msg_error "Not found image file: $IMAGE_PATH/$1"
[ "$2" = "Essential" ] && exit -1
return 1
fi
}
function create_package_file()
{
IFS=,
for part in $SPECIAL_PARTITIONS $PARTITIONS;
do
case $part in
package-file)
PACKAGE_CFG="$part package-file\n$PACKAGE_CFG"
;;
bootloader)
__package_file download.bin Essential
;;
parameter)
__package_file $PARTITION_FILE Essential
;;
rootfs)
__package_file $part
[ $? -eq 1 ] && __package_file rootfs_base.img
;;
*)
__package_file $part
;;
esac
done
IFS=
msg_info "partitions:\n$PACKAGE_CFG"
echo -e $PACKAGE_CFG > $IMAGE_PATH/package-file
}
CHIP_ID=""
IMAGE_PATH=""
PARTITION_FILE=""
SPECIAL_PARTITIONS="bootloader,package-file"
PARTITIONS=""
PACKAGE_CFG=""
ENV_ALL_OPTS=""
chk_param $@
msg_info "partition file name: $PARTITION_FILE"
msg_info "start to package update.img..."
get_partition_names
create_package_file
echo "TOOL_PATH=$cwd"
$cwd/afptool -pack $IMAGE_PATH $IMAGE_PATH/update_tmp.img $ENV_ALL_OPTS || pause
$cwd/rkImageMaker $CHIP_ID $IMAGE_PATH/download.bin $IMAGE_PATH/update_tmp.img $IMAGE_PATH/update.img -os_type:androidos || pause
msg_info "Making $CHIP_ID update.img OK."
# remove temporay image
rm -f $IMAGE_PATH/update_tmp.img
rm -f $IMAGE_PATH/package-file
exit $?