#!/bin/bash #==================================================================================== # # Function: Update the kernel for OpenWrt (Amlogic s9xxx, Allwinner, Rockchip) # Copyright (C) 2020-- https://github.com/unifreq/openwrt_packit # Copyright (C) 2021-- https://github.com/ophub/luci-app-amlogic # # Support the kernel: boot-*.tar.gz, dtb-*.tar.gz, modules-*.tar.gz # It is recommended to install MAINLINE_UBOOT for kernel versions above 5.10.y # openwrt-kernel ${AUTO_MAINLINE_UBOOT} # E.g: openwrt-kernel yes # openwrt-kernel no # #================================== Functions list ================================== # # error_msg : Output error message # get_textoffset : Get kernel TEXT_OFFSET # init_var : Initialize all variables # check_kernel : Check kernel files list # chech_files_same : Check file consistency # restore_kernel : Restore current kernel # update_kernel : Update the kernel # update_uboot : Update the uboot # #============================== Set default parameters ============================== # # Receive one-key command related parameters AUTO_MAINLINE_UBOOT="no" # Set the release check file release_file="/etc/flippy-openwrt-release" # #==================================================================================== # Encountered a serious error, abort the script execution error_msg() { echo -e "[Error] ${1}" exit 1 } # Get the partition name of the root file system get_root_partition_name() { local paths=("/" "/overlay" "/rom") local partition_name for path in "${paths[@]}"; do partition_name=$(df "${path}" | awk 'NR==2 {print $1}' | awk -F '/' '{print $3}') [[ -n "${partition_name}" ]] && break done [[ -z "${partition_name}" ]] && error_msg "Cannot find the root partition!" echo "${partition_name}" } # Get kernel TEXT_OFFSET, For u-boot.ext and u-boot.emmc get_textoffset() { boot_tgz_file="${1}" vmlinuz_name="${2}" K510="1" temp_dir="$(mktemp -d)" ( cd ${temp_dir} tar -xf "${boot_tgz_file}" "${vmlinuz_name}" ) # With TEXT_OFFSET patch is [ 0108 ], without TEXT_OFFSET patch is [ 0000 ] [[ "$(hexdump -n 15 -x "${temp_dir}/${vmlinuz_name}" 2>/dev/null | head -n 1 | awk '{print $7}')" == "0108" ]] && K510="0" } init_var() { # Receive one-key command related parameters [[ "${1}" == "yes" ]] && AUTO_MAINLINE_UBOOT="yes" # Check dependencies [[ -n "$(busybox which tar)" ]] || error_msg "Missing [ tar ] in OpenWrt firmware, unable to update kernel" # Check release file if [[ -s "${release_file}" ]]; then source "${release_file}" PLATFORM="${PLATFORM}" MODEL_ID="${MODEL_ID}" UBOOT_OVERLOAD="${UBOOT_OVERLOAD}" MAINLINE_UBOOT="${MAINLINE_UBOOT}" ANDROID_UBOOT="${ANDROID_UBOOT}" SOC="${SOC}" LOCK_KERNEL="${LOCK_KERNEL}" else error_msg "${release_file} file is missing!" fi [[ -n "${PLATFORM}" ]] || error_msg "Missing ${PLATFORM} value in ${release_file} file." # Define supported platforms support_platform=("allwinner" "rockchip" "amlogic" "qemu-aarch64") [[ -n "$(echo "${support_platform[@]}" | grep -w "${PLATFORM}")" ]] || error_msg "[ ${PLATFORM} ] is not supported." # Set /boot/vmlinuz-* replication names for different SoCs MYBOOT_VMLINUZ="$(ls -l /boot/*Image 2>/dev/null | awk '{print $9}' | head -n 1)" MYBOOT_VMLINUZ="${MYBOOT_VMLINUZ##*/}" [[ -n "${MYBOOT_VMLINUZ}" ]] || error_msg "Failed to get Image name: [ ${MYBOOT_VMLINUZ} ]" # Find the partition where root is located ROOT_PTNAME="$(get_root_partition_name)" # Find the disk where the partition is located, only supports mmcblk?p? sd?? hd?? vd?? and other formats case "${ROOT_PTNAME}" in mmcblk?p[1-9]) EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-2)}')" PARTITION_NAME="p" ;; [hsv]d[a-z][1-9]) EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-1)}')" PARTITION_NAME="" ;; nvme?n?p[1-9]) EMMC_NAME="$(echo ${ROOT_PTNAME} | awk '{print substr($1, 1, length($1)-2)}')" PARTITION_NAME="p" ;; *) error_msg "Unable to recognize the disk type of ${ROOT_PTNAME}!" ;; esac P4_PATH="/mnt/${EMMC_NAME}${PARTITION_NAME}4" # Move kernel related files to the ${P4_PATH} directory mv -f /tmp/upload/* ${P4_PATH} 2>/dev/null # Current device model MYDEVICE_NAME="$(cat /proc/device-tree/model | tr -d '\000')" [[ "${PLATFORM}" == "qemu-aarch64" ]] && MYDEVICE_NAME="KVM Virtual Machine" echo -e "Current device: ${MYDEVICE_NAME} [ ${PLATFORM} ], Use in [ ${EMMC_NAME} ]" sync && echo "" } # Check kernel files list check_kernel() { cd ${P4_PATH} # Determine custom kernel filename kernel_boot="$(ls boot-*.tar.gz 2>/dev/null | head -n 1)" kernel_name="${kernel_boot:5:-7}" KERNEL_VERSION="$(echo ${kernel_name} | grep -oE '^[1-9].[0-9]{1,3}.[0-9]+')" echo -e "Kernel name: ${kernel_name}" # check if kernel is locked if [ -n "${LOCK_KERNEL}" ]; then if [ "${LOCK_KERNEL}" != "${kernel_name}" ]; then if ! echo "${kernel_name}" | grep -E '^5.10.\d+-.*?rk35.*?$' >/dev/null; then error_msg "The kernel version is locked to [ ${LOCK_KERNEL} ], but your kernel is [ ${kernel_name} ]. " fi fi fi # Check if the file is added with TEXT_OFFSET patch get_textoffset "${P4_PATH}/${kernel_boot}" "vmlinuz-${kernel_name}" echo -e "K510 [ ${K510} ]" if [[ "${PLATFORM}" == "amlogic" && "${K510}" -eq "1" ]]; then [[ -n "${UBOOT_OVERLOAD}" && -f "/boot/${UBOOT_OVERLOAD}" ]] || error_msg "The UBOOT_OVERLOAD file is missing and cannot be update." fi # Check the sha256sums file sha256sums_file="sha256sums" sha256sums_check="1" [[ -s "${sha256sums_file}" && -n "$(cat ${sha256sums_file})" ]] || sha256sums_check="0" [[ -n "$(busybox which sha256sum)" ]] || sha256sums_check="0" [[ "${sha256sums_check}" -eq "1" ]] && echo -e "Enable sha256sum checking..." # Loop check file i="1" if [[ "${PLATFORM}" == "qemu-aarch64" ]]; then kernel_list=("boot" "modules") else kernel_list=("boot" "dtb-${PLATFORM}" "modules") fi for kernel_file in ${kernel_list[*]}; do # Set check filename tmp_file="${kernel_file}-${kernel_name}.tar.gz" # Check if file exists [[ -s "${tmp_file}" ]] || error_msg "The [ ${kernel_file} ] file is missing." # Check if the file sha256sum is correct if [[ "${sha256sums_check}" -eq "1" ]]; then tmp_sha256sum="$(sha256sum "${tmp_file}" | awk '{print $1}')" tmp_checkcode="$(cat ${sha256sums_file} | grep ${tmp_file} | awk '{print $1}')" [[ "${tmp_sha256sum}" == "${tmp_checkcode}" ]] || error_msg "${tmp_file}: sha256sum verification failed." echo -e "(${i}/3) [ ${tmp_file} ] file sha256sum check same." fi let i++ done sync && echo "" } # Check the consistency of amlogic device files chech_files_same() { i="0" max_try="5" while [[ "${i}" -le "${max_try}" ]]; do if [[ "$(sha256sum "${1}" | awk '{print $1}')" == "$(sha256sum "${2}" | awk '{print $1}')" ]]; then echo "" && break else cp -f ${1} ${2} i="$((i + 1))" fi done [[ "${i}" -gt "${max_try}" ]] && echo "God, it's different after ${max_try} copies: [ ${1} ]" } # Restore the kernel when the update fails restore_kernel() { ( cd /boot rm -rf \ config-${kernel_name} \ System.map-${kernel_name} \ initrd.img-${kernel_name} \ uInitrd-${kernel_name} \ vmlinuz-${kernel_name} \ initrd.img \ uInitrd \ zImage \ Image \ vmlinuz \ dtb* 2>/dev/null tar -xzf /tmp/boot-backup.tar.gz 2>/dev/null ) error_msg "Kernel update failed and has been reverted." } # Update the kernel update_kernel() { local cur_kernel_name=$(uname -r) local boot_fstype=$(df -T /boot | tail -n1 | awk '{print $2}') echo -e "Start unpacking the kernel..." # 01. for /boot five files # Backup the current_kernel ( cd /boot tar -czf /tmp/boot-backup.tar.gz \ config-${cur_kernel_name} \ System.map-${cur_kernel_name} \ initrd.img-${cur_kernel_name} \ uInitrd-${cur_kernel_name} \ vmlinuz-${cur_kernel_name} \ initrd.img \ uInitrd \ zImage \ Image \ vmlinuz \ dtb* 2>/dev/null rm -rf \ config-${cur_kernel_name} \ System.map-${cur_kernel_name} \ initrd.img-${cur_kernel_name} \ uInitrd-${cur_kernel_name} \ vmlinuz-${cur_kernel_name} \ initrd.img \ uInitrd \ zImage \ Image \ vmlinuz \ dtb* 2>/dev/null ) # Extract the new kernel tar -xf ${P4_PATH}/boot-${kernel_name}.tar.gz -C /boot # Check if the file exists local valid_files if [[ "${PLATFORM}" == "qemu-aarch64" ]]; then valid_files="vmlinuz-${kernel_name} initrd.img-${kernel_name} config-${kernel_name} System.map-${kernel_name}" rm -f /boot/uInitrd* else valid_files="vmlinuz-${kernel_name} uInitrd-${kernel_name} config-${kernel_name} System.map-${kernel_name}" rm -f /boot/initrd.img* fi for f in ${valid_files}; do [[ -f "/boot/${f}" ]] || restore_kernel; done # Check if the files are the same ( cd /boot if [[ "${PLATFORM}" == "qemu-aarch64" ]]; then ln -sf initrd.img-${kernel_name} initrd.img ln -sf vmlinuz-${kernel_name} ${MYBOOT_VMLINUZ} elif [[ "$boot_fstype" == "vfat" ]]; then cp -f uInitrd-${kernel_name} uInitrd [[ -z "$(chech_files_same uInitrd-${kernel_name} uInitrd)" ]] || restore_kernel cp -f vmlinuz-${kernel_name} ${MYBOOT_VMLINUZ} [[ -z "$(chech_files_same vmlinuz-${kernel_name} ${MYBOOT_VMLINUZ})" ]] || restore_kernel else ln -sf uInitrd-${kernel_name} uInitrd ln -sf vmlinuz-${kernel_name} ${MYBOOT_VMLINUZ} fi # wxy-oect: MODEL_ID numbers r304 and r306, require special handling of uInitrd [[ "${MODEL_ID}" == "r304" || "${MODEL_ID}" == "r306" ]] && { rm -f uInitrd && ln -sf initrd.img-${kernel_name} uInitrd } ) echo -e "(1/3) Unpacking [ boot-${kernel_name}.tar.gz ] done." if [[ "${PLATFORM}" == "qemu-aarch64" ]]; then echo -e "(2/3) skip unpack dtb files." else # 02. for /boot/dtb/${PLATFORM}/* if [[ "${boot_fstype}" == "vfat" ]]; then (cd /boot && mkdir -p dtb/${PLATFORM}) else (cd /boot && mkdir -p dtb-${kernel_name}/${PLATFORM} && ln -sf dtb-${kernel_name} dtb) fi tar -xf ${P4_PATH}/dtb-${PLATFORM}-${kernel_name}.tar.gz -C /boot/dtb/${PLATFORM} [[ "$(ls /boot/dtb/${PLATFORM} -l 2>/dev/null | grep "^-" | wc -l)" -ge "1" ]] || error_msg "/boot/dtb/${PLATFORM} file is missing." echo -e "(2/3) Unpacking [ dtb-${PLATFORM}-${kernel_name}.tar.gz ] done." fi # 03. for /lib/modules/* rm -rf /lib/modules/* tar -xf ${P4_PATH}/modules-${kernel_name}.tar.gz -C /lib/modules ( cd /lib/modules/${kernel_name} rm -f *.ko find ./ -type f -name '*.ko' -exec ln -s {} ./ \; sync && sleep 3 x=$(ls *.ko -l 2>/dev/null | grep "^l" | wc -l) [[ "${x}" -eq "0" ]] && error_msg "Error *.ko Files not found." ) echo -e "(3/3) Unpacking [ modules-${kernel_name}.tar.gz ] done." # Delete kernel tmpfiles rm -f ${P4_PATH}/*-${kernel_name}.tar.gz rm -f ${P4_PATH}/sha256sums sync && echo "" } # Update the uboot update_uboot() { # Only amlogic SoCs needs to be updated if [[ "${PLATFORM}" == "amlogic" ]]; then # Copy u-boot.ext and u-boot.emmc if [[ "${K510}" -eq "1" && -n "${UBOOT_OVERLOAD}" && -f "/boot/${UBOOT_OVERLOAD}" ]]; then [[ ! -f "/boot/u-boot.ext" ]] && cp -f "/boot/${UBOOT_OVERLOAD}" /boot/u-boot.ext && chmod +x /boot/u-boot.ext [[ ! -f "/boot/u-boot.emmc" ]] && cp -f "/boot/u-boot.ext" /boot/u-boot.emmc && chmod +x /boot/u-boot.emmc echo -e "The ${UBOOT_OVERLOAD} file copy is complete." elif [[ "${K510}" -eq "0" ]]; then rm -f "/boot/u-boot.ext" "/boot/u-boot.emmc" fi # Write Mainline bootloader if [[ -f "${MAINLINE_UBOOT}" && "${AUTO_MAINLINE_UBOOT}" == "yes" ]]; then echo -e "Write Mainline bootloader: [ ${MAINLINE_UBOOT} ] to [ /dev/${EMMC_NAME} ]" dd if=${MAINLINE_UBOOT} of=/dev/${EMMC_NAME} bs=1 count=442 conv=fsync dd if=${MAINLINE_UBOOT} of=/dev/${EMMC_NAME} bs=512 skip=1 seek=1 conv=fsync echo -e "The MAINLINE_UBOOT file write is complete." fi fi # Update release file sed -i "s|^KERNEL_VERSION=.*|KERNEL_VERSION='${kernel_name}'|g" ${release_file} 2>/dev/null # Update banner file sed -i "s| Kernel.*| Kernel: ${kernel_name}|g" /etc/banner 2>/dev/null sync && echo "" } # Rescue the kernel sos_kernel() { echo -e "Start rescuing kernel..." # Supports specifying disks, such as: [ openwrt-kernel -s mmcblk1 ] box_disk="${2}" if [[ -n "${box_disk}" ]]; then # Format the disk names box_disk="${box_disk//\/dev\//}" # Check if the disk exists [[ -b "/dev/${box_disk}" ]] || error_msg "The specified disk [ ${box_disk} ] does not exist." # Check if the disk is the same as the current system disk [[ "${box_disk}" == "${EMMC_NAME}" ]] && error_msg "The specified disk [ ${box_disk} ] is the same as the current system disk [ ${EMMC_NAME} ]." echo -e "The device name of the specified disk: [ ${box_disk} ]" else # Find emmc disk, first find emmc containing boot0 partition box_disk="$(lsblk -l -o NAME | grep -oE '(mmcblk[0-9]?|nvme[0-9]?n[0-9]?|[hsv]d[a-z])' | grep -vE ^${EMMC_NAME} | sort -u | head -n 1)" # Check if disk exists [[ -z "${box_disk}" ]] && error_msg "Unable to locate the storage requiring rescue." echo -e "The device name of the target disk: [ ${box_disk} ]" fi rescue_disk="/dev/${box_disk}" echo -e "The current OpenWrt is running on [ /dev/${EMMC_NAME} ], and the target disk for restoration is [ ${rescue_disk} ]." # Create a temporary mount directory umount ${P4_PATH}/bootfs 2>/dev/null umount ${P4_PATH}/rootfs 2>/dev/null rm -rf ${P4_PATH}/bootfs ${P4_PATH}/rootfs 2>/dev/null mkdir -p ${P4_PATH}/{bootfs/,rootfs/} && sync [[ "${?}" -ne "0" ]] && error_msg "Failed to create temporary mount directory [ ${P4_PATH} ]" # Mount target bootfs partition [[ "${box_disk}" =~ ^([hsv]d[a-z]) ]] && rescue_disk_partition_name="" || rescue_disk_partition_name="p" mount ${rescue_disk}${rescue_disk_partition_name}1 ${P4_PATH}/bootfs [[ "${?}" -ne "0" ]] && error_msg "mount ${rescue_disk}${PARTITION_NAME}1 failed!" echo -e "The [ ${rescue_disk}${rescue_disk_partition_name}1 ] partition is mounted on [ ${P4_PATH}/bootfs ]." # Search uuid file if [[ -f "${P4_PATH}/bootfs/uEnv.txt" ]]; then search_file="uEnv.txt" elif [[ -f "${P4_PATH}/bootfs/armbianEnv.txt" ]]; then search_file="armbianEnv.txt" elif [[ -f "${P4_PATH}/bootfs/extlinux/extlinux.conf" ]]; then search_file="extlinux/extlinux.conf" else error_msg "The [ uEnv.txt, armbianEnv.txt, extlinux/extlinux.conf ] file does not exist, stop rescuing." fi # Get the target partition uuid and rootfs target_parttion_uuid="$(grep '=UUID=' ${P4_PATH}/bootfs/${search_file} | sed -n 's/.*=UUID=\([a-f0-9-]*\).*/\1/p')" [[ -z "${target_parttion_uuid}" ]] && error_msg "The [ ${search_file} ] file does not contain the UUID value." target_rootfs="$(blkid | grep ${target_parttion_uuid} | awk -F':' '{print $1;}')" [[ -z "${target_rootfs}" ]] && error_msg "The [ ${target_parttion_uuid} ] UUID does not exist in the system." # Mount target rootfs partition mount ${target_rootfs} ${P4_PATH}/rootfs [[ "${?}" -ne "0" ]] && error_msg "mount ${rescue_disk}${PARTITION_NAME}2 failed!" echo -e "The [ ${target_rootfs} ] partition is mounted on [ ${P4_PATH}/rootfs ]." # Identify the current kernel files kernel_signature="$(uname -r)" # 01. For /boot files [[ -d "${P4_PATH}/bootfs" ]] && { cd ${P4_PATH}/bootfs rm -rf config-* initrd.img-* System.map-* vmlinuz-* uInitrd* *Image dtb* u-boot.ext u-boot.emmc [[ -f "/boot/u-boot.ext" ]] && { cp -f /boot/u-boot.ext . cp -f /boot/u-boot.ext u-boot.emmc chmod +x u-boot.ext u-boot.emmc } cp -rf /boot/{*-${kernel_signature},uInitrd,*Image,dtb} . [[ "${?}" -ne "0" ]] && error_msg "(1/2) [ boot ] kernel files rescue failed." echo -e "(1/2) [ boot ] kernel files rescue succeeded." [[ -f "/boot/emmc_autoscript.cmd" ]] && cp -f /boot/emmc_autoscript.cmd . [[ -f "/boot/emmc_autoscript" ]] && cp -f /boot/emmc_autoscript . [[ -f "/boot/s905_autoscript.cmd" ]] && cp -f /boot/s905_autoscript.cmd . [[ -f "/boot/s905_autoscript" ]] && cp -f /boot/s905_autoscript . } || error_msg "(1/2) The [ ${P4_PATH}/bootfs ] folder does not exist, stop rescuing." # 02. For /lib/modules/${kernel_signature} [[ -d "${P4_PATH}/rootfs/lib/modules" ]] && { cd ${P4_PATH}/rootfs/lib/modules rm -rf * cp -rf /lib/modules/${kernel_signature} . [[ "${?}" -ne "0" ]] && error_msg "(2/2) [ modules ] kernel files rescue failed." echo -e "(2/2) [ modules ] kernel files rescue succeeded." } || error_msg "(2/2) The [ ${P4_PATH}/rootfs/lib/modules ] folder does not exist, stop rescuing." # Unmount the emmc partition cd ${P4_PATH} umount -f ${P4_PATH}/bootfs [[ "${?}" -ne "0" ]] && error_msg "Failed to umount [ ${P4_PATH}/bootfs ]" umount -f ${P4_PATH}/rootfs [[ "${?}" -ne "0" ]] && error_msg "Failed to umount [ ${P4_PATH}/rootfs ]" # Remove the temporary mount directory rm -rf ${P4_PATH}/bootfs ${P4_PATH}/rootfs sync && echo "" } echo -e "Welcome to the OpenWrt Kernel Management Tool." # Operation environment check [[ -x "/usr/sbin/openwrt-kernel" ]] || error_msg "Please grant execution permission: chmod +x /usr/sbin/openwrt-kernel" # Execute relevant functions based on the options if [[ "${@}" =~ ^-s(\s)* ]]; then # Initialize all variables init_var "${@}" # Start rescuing the kernel sos_kernel "${@}" # Kernel restore successful sync && sleep 3 echo -e "Kernel rescue successful, please remove the disk and restart the OpenWrt system." exit 0 else # Initialize all variables init_var "${@}" # Check kernel files list check_kernel # Update the kernel update_kernel # Update the uboot update_uboot # Kernel update successful sync && sleep 3 echo "Successfully updated, automatic restarting..." reboot exit 0 fi