Rockchip RK3588 - Rockchip Linux Recovery recovery源码分析

----------------------------------------------------------------------------------------------------------------------------

开发板 :NanoPC-T4开发板eMMC16GBLPDDR34GB
显示屏 :15.6英寸HDMI接口显示屏uboot2017.09linux4.19
----------------------------------------------------------------------------------------------------------------------------

在《Rockchip RK3588 - Rockchip Linux Recovery updateEngine源码分析》中我们对updateEngine源码进行了深入分析,并提到updateEngine 升级命令由两部分组成;

  • normal系统下的升级:升级recovery分区,并修改misc分区,重启系统;
  • recovery系统下的升级:系统重启之后,会根据misc分区存放的字段来判断将要引导的系统是normal系统还是recovery系统,这里会进入到recovery系统,进行剩余分区的升级;

其中normal系统下的升级是通过updateEngine可执行程序完成的;

// 1. 在normal系统,updateEngine升级recovery分区;升级完函数就返回了,并不会升级其它分区
main(argc, argv)
	MiscUpdate(image_url, partition, save_path)		
		//  对需要执行的升级命令打标记(这里标记了parameter、recovery升级命令)
		RK_ota_set_partition(0x040000)   
		// 进行parameter、recovery分区升级
		RK_ota_start(handle_upgrade_callback, handle_print_callback)
		// 往misc偏移16k位置写入recovery信息,这样系统重启后会进入recovery系统执行剩余分区的升级
		set_bootloader_message(&msg)
		
	// 2. 触发系统重启
	system(" echo b > /proc/sysrq-trigger ")

recovery系统下的升级,是通过recovery可执行程序实现,具体命令如下:

/usr/bin/recovery --update_package=/userdata/update.img

recovery二进制bin程序部会根据编译配置调用updateEngine或者rkupdate进行升级,本章的目标就是对recovery源码进行分析,实验基于《Rockchip RK3399 - 从零开始制作recovery系统》中移植的recovery系统

一、recovery目标分析

1.1 目标recovery

定位到<Rockchip Linux SDK>/external/recovery目录下的Makefile文件,找到目标recovery;

OBJ = recovery.o \
        default_recovery_ui.o \
        rktools.o \
        roots.o \
        bootloader.o \
        safe_iop.o \
        strlcpy.o \
        strlcat.o \
        rkupdate.o \
        sdboot.o \
        usbboot.o \
        mtdutils/mounts.o \
        mtdutils/mtdutils.o \
        mtdutils/rk29.o \
        minzip/DirUtil.o \
        update_engine/log.o

ifdef RecoveryNoUi  
OBJ += noui.o      # 不走这里
else
OBJ += ui.o\       # 走这里  
        minzip/Hash.o \
        minzip/SysUtil.o \
        minzip/Zip.o \
        minui/events.o \
        minui/graphics.o \
        minui/resources.o \
        minui/graphics_drm.o
endif

CFLAGS += -I$(PROJECT_DIR) -I/usr/include -I/usr/include/libdrm/ -lc -DUSE_UPDATEENGINE=ON

ifdef RecoveryNoUi
CFLAGS += -lpthread -lbz2        # 不走这里
else
CFLAGS += -lz -lpng -ldrm -lpthread -lcurl -lcrypto -lbz2   # 走这里  
endif

$(PROM): $(OBJ)
        $(CC) -o $(PROM) $(OBJ) $(CFLAGS)

可以看到目标recovery是由若干个.o文件通过aarch64-buildroot-linux-gnu-gcc编译器链接生成的可执行文件。

.o文件实际上是由.c文件通过aarch64-buildroot-linux-gnu-gcc编译器编译生成的。

# build in buildroot, it need change work directory
recovery_version:
        cd $(PROJECT_DIR)/../../../../../external/recovery && \
        COMMIT_HASH=$$(git rev-parse --verify --short HEAD) && \
        GIT_COMMIT_TIME=$$(git log -1 --format=%cd --date=format:%y%m%d) && \
        GIT_DIRTY=$$(git diff-index --quiet HEAD -- || echo "-dirty") && \
        commit_info=-g$${COMMIT_HASH}-$${GIT_COMMIT_TIME}$${GIT_DIRTY} && \
        cd $(PROJECT_DIR) && \
        echo "#define GIT_COMMIT_INFO $${commit_info}" > recovery_autogenerate.h

%.o: %.cpp
        $(CC) -c $< -o $@ $(CFLAGS)

%.o: %.c recovery_version
        $(CC) -c $< -o $@ $(CFLAGS)

其中:%.o 表示所有以.o结尾的文件作为目标文件,%.cpp 表示所有以.c结尾的文件作为依赖文件。

1.2 编译

在《Rockchip RK3588 - Rockchip Linux SDK Buildroot文件系统构建》中介绍过buildroot编译命令;

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ sudo make -j8

或者使用如下命令单独编译recovery软件包:

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ sudo make recovery-dirclean
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot$ sudo make recovery

其中recovery编译日志如下:

>>> recovery develop Syncing from source dir /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/../external/recovery
rsync -au --chmod=u=rwX,go=rX  --exclude .svn --exclude .git --exclude .hg --exclude .bzr --exclude CVS /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/../external/recovery/ /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop
>>> recovery develop Configuring
>>> recovery develop Building
PATH="/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" /usr/bin/make  -C /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop CC="/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-gcc" CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -I. -fPIC -lpthread -lcurl -lssl -lcrypto -lbz2 -lpng -ldrm -lz -lm -I/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/aarch64-buildroot-linux-gnu/sysroot/usr/include/libdrm -DUSE_UPDATEENGINE=ON -DSUCCESSFUL_BOOT=ON"
make[1]: 进入目录“/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop”
cd /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop/../../../../../external/recovery && \
COMMIT_HASH=$(git rev-parse --verify --short HEAD) && \
GIT_COMMIT_TIME=$(git log -1 --format=%cd --date=format:%y%m%d) && \
GIT_DIRTY=$(git diff-index --quiet HEAD -- || echo "-dirty") && \
commit_info=-g${COMMIT_HASH}-${GIT_COMMIT_TIME}${GIT_DIRTY} && \
cd /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop && \
echo "#define GIT_COMMIT_INFO ${commit_info}" > recovery_autogenerate.h
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-gcc -c recovery.c -o recovery.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -I. -fPIC -lpthread -lcurl -lssl -lcrypto -lbz2 -lpng -ldrm -lz -lm -I/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/aarch64-buildroot-linux-gnu/sysroot/usr/include/libdrm -DUSE_UPDATEENGINE=ON -DSUCCESSFUL_BOOT=ON
......
/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/bin/aarch64-buildroot-linux-gnu-gcc -o updateEngine mtdutils/mounts.o mtdutils/mtdutils.o mtdutils/rk29.o update_engine/rkbootloader.o update_engine/download.o update_engine/flash_image.o update_engine/log.o update_engine/main.o update_engine/md5sum.o update_engine/rkimage.o update_engine/rktools.o update_engine/rkboot.o update_engine/crc.o update_engine/update.o update_engine/do_patch.o -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os -g0 -D_FORTIFY_SOURCE=1 -I. -fPIC -lpthread -lcurl -lssl -lcrypto -lbz2 -lpng -ldrm -lz -lm -I/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/aarch64-buildroot-linux-gnu/sysroot/usr/include/libdrm -DUSE_UPDATEENGINE=ON -DSUCCESSFUL_BOOT=ON
make[1]: 离开目录“/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop”
>>> recovery develop Installing to target
/usr/bin/install -D -m 755 /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop/recovery /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/usr/bin/
mkdir -p /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/res/images
cp /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop/res/images/* /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/res/images/
/usr/bin/install -D -m 755 /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/build/recovery-develop/updateEngine /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/usr/bin/
/usr/bin/install -D -m 755 package/rockchip/recovery//S40recovery /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/target/etc/init.d/S40recovery

二、recovery源码分析

recovery程序的入口为recovery.c文件。recovery.c代码比较长,如下所示:

点开查看详情
int main(int argc, char **argv)
{
    bool bSDBoot    = false;
    bool bUDiskBoot = false;
    const char *sdupdate_package = NULL;
    const char *usbupdate_package = NULL;
    int previous_runs = 0;
    const char *send_intent = NULL;
    const char *update_package = NULL;
    const char *encrypted_fs_mode = NULL;
    int wipe_data = 0;
    int wipe_all = 0;
    int pcba_test = 0;  // add for pcba test
    int toggle_secure_fs = 0;
    int arg;
    bool isrkdebug = false;
    int log_level = LOG_DEBUG;
    encrypted_fs_info encrypted_fs_data;
    struct timeval start_time, end_time;
    long long elapsed_time;

    gettimeofday(&start_time, NULL);
    
    // 获取命令行参数
    get_args(&argc, &argv);
    strcpy(systemFlag, "false");
    
    // 解析命令行参数
    while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
        switch (arg) {
        case 'p':
            previous_runs = atoi(optarg);
            break;
        case 's':
            send_intent = optarg;
            break;
        case 'u':
            update_package = optarg;
            break;
        case 'w':
            wipe_data = 1;
            break;
        case 'a':
            wipe_all = 1;
            break;
        case 'e':
            encrypted_fs_mode = optarg;
            toggle_secure_fs = 1;
            break;
        case 't':
            ui_show_text(1);
            break;
        case 'f':
            pcba_test = 1;
            break;  // add for pcba test
        case 'r':
            isrkdebug = true;
            break;
        case 'i':
            gr_set_rotate(atoi(optarg));
            break;
        case '?':
            LOGE("Invalid command argument\n");
            continue;
        }
    }
    
    time_t start = time(NULL);
    if ((access("/.rkdebug", F_OK) != 0) && (isrkdebug != true)) {
        // If these fail, there's not really anywhere to complain...
        if (freopen(TEMPORARY_LOG_FILE, "a", stdout) == NULL) {
            LOGW("freopen stdout error");
        }
        setbuf(stdout, NULL);
        if (freopen(TEMPORARY_LOG_FILE, "a", stderr) == NULL) {
            LOGE("freopen stderr error");
        }
        setbuf(stderr, NULL);
    }
    
    printf("\n");
    printf("*********************************************************\n");
    printf("            ROCKCHIP recovery system                     \n");
    printf("*********************************************************\n");
    printf("**** version : %s ****\n", recovery_version);
    
    LOGI("Starting recovery on %s\n", ctime(&start));
    while (access(coldboot_done, F_OK) != 0) {
        LOGI("coldboot not done, wait...\n");
        sleep(1);
    }

#ifndef RecoveryNoUi
    LOGI("Recovery System have UI defined.\n");
#endif

    ui_init();
    ui_set_background(BACKGROUND_ICON_INSTALLING);
    load_volume_table();
    setFlashPoint();
    
    bSDBoot = is_boot_from_SD();
    bUDiskBoot = is_boot_from_udisk();
    
    if (bSDBoot || bUDiskBoot) {
        char imageFile[64] = {0};
        if (bSDBoot) {
            if (is_sdcard_update()) {
                strlcpy(imageFile, EX_SDCARD_ROOT, sizeof(imageFile));
                strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));
                if (access(imageFile, F_OK) == 0) {
                    sdupdate_package = strdup(imageFile);
                    bSDBootUpdate = true;
                    ui_show_text(1);
                    LOGI("sdupdate_package = %s\n", sdupdate_package);
                }
            }
        }
    
        if (bUDiskBoot) {
            if (is_udisk_update()) {
                strlcpy(imageFile, EX_UDISK_ROOT, sizeof(imageFile));
                strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));
                if (access(imageFile, F_OK) == 0) {
                    usbupdate_package = strdup(imageFile);
                    bUdiskUpdate = true;
                    ui_show_text(1);
                    LOGI("usbupdate_package = %s\n", usbupdate_package);
                }
            }
        }
    }
    
    device_recovery_start();
    
    LOGI("Command:");
    for (arg = 0; arg < argc; arg++) {
        printf(" \"%s\"", argv[arg]);
    }
    printf("\n");
    
    if (update_package) {
        // For backwards compatibility on the cache partition only, if
        // we're given an old 'root' path "CACHE:foo", change it to
        // "/cache/foo".
        if (strncmp(update_package, "CACHE:", 6) == 0) {
            int len = strlen(update_package) + 10;
            char* modified_path = malloc(len);
            strlcpy(modified_path, "/cache/", len);
            strlcat(modified_path, update_package + 6, len);
            LOGI("(replacing path \"%s\" with \"%s\")\n",
                 update_package, modified_path);
            update_package = modified_path;
        }
    }
    printf("\n");
    
    int status = INSTALL_SUCCESS;
    
    if (toggle_secure_fs) {
        if (strcmp(encrypted_fs_mode, "on") == 0) {
            encrypted_fs_data.mode = MODE_ENCRYPTED_FS_ENABLED;
            ui_print("Enabling Encrypted FS.\n");
        } else if (strcmp(encrypted_fs_mode, "off") == 0) {
            encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
            ui_print("Disabling Encrypted FS.\n");
        } else {
            ui_print("Error: invalid Encrypted FS setting.\n");
            status = INSTALL_ERROR;
        }
    
        // Recovery strategy: if the data partition is damaged, disable encrypted file systems.
        // This preventsthe device recycling endlessly in recovery mode.
        if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
            (read_encrypted_fs_info(&encrypted_fs_data))) {
            ui_print("Encrypted FS change aborted, resetting to disabled state.\n");
            encrypted_fs_data.mode = MODE_ENCRYPTED_FS_DISABLED;
        }
    
        if (status != INSTALL_ERROR) {
            if (erase_volume("/userdata")) {
                ui_print("Data wipe failed.\n");
                status = INSTALL_ERROR;
#if 0
            } else if (erase_volume("/cache")) {
                ui_print("Cache wipe failed.\n");
                status = INSTALL_ERROR;
#endif
            } else if ((encrypted_fs_data.mode == MODE_ENCRYPTED_FS_ENABLED) &&
                       (restore_encrypted_fs_info(&encrypted_fs_data))) {
                ui_print("Encrypted FS change aborted.\n");
                status = INSTALL_ERROR;
            } else {
                ui_print("Successfully updated Encrypted FS.\n");
                status = INSTALL_SUCCESS;
            }
        }
    } else if (update_package != NULL) {
        int i, ret = 0;
        const char* binary = "/usr/bin/rkupdate";

        rockchip_partition_check();
    
        for (i = 0; i < 5; i++) {
            if (!ensure_path_mounted(update_package)) {
                LOGI("mounted %s Success.\n", update_package);
                break;
            }
            LOGW("mounted %s Failed. retry %d\n", update_package, i + 1);
            sleep(1);
        }
        if (i != 5) {
            LOGI(">>>rkflash will update from %s\n", update_package);
#ifdef USE_RKUPDATE
            status = do_rk_update(binary, update_package);
#endif
#ifdef USE_UPDATEENGINE
            const char* updateEnginebin = "/usr/bin/updateEngine";
            status = do_rk_updateEngine(updateEnginebin, update_package);
#endif
            if (status == INSTALL_SUCCESS) {
                strcpy(systemFlag, update_package);
                /* update success, delete update.img. */
                if (access(update_package, F_OK) == 0)
                    remove(update_package);
                ui_print("update.img images success!\n");
            } else {
                ui_print("update.img images failed!\n");
            }
        } else {
            LOGE("mounted %s Failed.\n", update_package);
            ui_print("mounted %s Failed.\n", update_package);
        }

        if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
        ui_print("update.img Installation done.\n");
        //ui_show_text(0);
    } else if (sdupdate_package != NULL) {
        rockchip_partition_check();
    
        // update image from sdcard
#ifdef USE_RKUPDATE
        const char* binary = "/usr/bin/rkupdate";
        LOGI(">>>sdboot update will update from %s\n", sdupdate_package);
        status = do_rk_update(binary, sdupdate_package);
#endif

#ifdef USE_UPDATEENGINE
#undef FACTORY_FIRMWARE_IMAGE
#undef CMD4RECOVERY_FILENAME
#define FACTORY_FIRMWARE_IMAGE "/mnt/sdcard/out_image.img"
#define CMD4RECOVERY_FILENAME "/mnt/sdcard/cmd4recovery"
        if ((access(FACTORY_FIRMWARE_IMAGE, F_OK)) && access(CMD4RECOVERY_FILENAME, F_OK)) {
            int tmp_fd = creat(CMD4RECOVERY_FILENAME, 0777);
            if (tmp_fd < 0) {
                LOGE("creat %s error.\n", CMD4RECOVERY_FILENAME);
                status = INSTALL_ERROR;
            } else {
                close(tmp_fd);
                const char* updateEnginebin = "/usr/bin/updateEngine";
                status = do_rk_updateEngine(updateEnginebin, sdupdate_package);
            }
        }

        if (isMtdDevice()) {
            LOGI("start flash write to /dev/mtd0.\n");
            size_t total_size;
            size_t erase_size;
            mtd_scan_partitions();
            const MtdPartition *part = mtd_find_partition_by_name("rk-nand");
            if ( part == NULL ) {
                part = mtd_find_partition_by_name("spi-nand0");
            }
            if (part == NULL || mtd_partition_info(part, &total_size, &erase_size, NULL)) {
                if ((!access(FACTORY_FIRMWARE_IMAGE, F_OK)) && mtd_find_partition_by_name("sfc_nor") != NULL) {
                    LOGI("Info: start flash out_image.img to spi nor.\n");
                    system("flashcp -v " FACTORY_FIRMWARE_IMAGE " /dev/mtd0");
                } else
                    LOGE("Error: Can't find rk-nand or spi-nand0.\n");
            } else {
                system("flash_erase /dev/mtd0 0x0 0");
                system("sh "CMD4RECOVERY_FILENAME);
            }
        } else {
            LOGI("Start to dd data to emmc partition.\n");
            system("sh "CMD4RECOVERY_FILENAME);
            LOGI("sdcard upgrade done\n");
        }

#endif

        if (status == INSTALL_SUCCESS) {
            LOGI("update.img Installation success.\n");
            ui_print("update.img Installation success.\n");
            //ui_show_text(0);
        }
    
    } else if (usbupdate_package != NULL) {
        rockchip_partition_check();
        // update image from udisk
#ifdef USE_RKUPDATE
        const char* binary = "/usr/bin/rkupdate";
        LOGI(">>>sdboot update will update from %s\n", usbupdate_package);
        status = do_rk_update(binary, usbupdate_package);
#endif

#ifdef USE_UPDATEENGINE
#undef FACTORY_FIRMWARE_IMAGE
#undef CMD4RECOVERY_FILENAME
#define FACTORY_FIRMWARE_IMAGE "/mnt/usb_storage/out_image.img"
#define CMD4RECOVERY_FILENAME "/mnt/usb_storage/cmd4recovery"
        if ((access(FACTORY_FIRMWARE_IMAGE, F_OK)) && access(CMD4RECOVERY_FILENAME, F_OK)) {
            int tmp_fd = creat(CMD4RECOVERY_FILENAME, 0777);
            if (tmp_fd < 0) {
                LOGE("creat %s error.\n", CMD4RECOVERY_FILENAME);
                status = INSTALL_ERROR;
            } else {
                close(tmp_fd);
                const char* updateEnginebin = "/usr/bin/updateEngine";
                status = do_rk_updateEngine(updateEnginebin, usbupdate_package);
            }
        }

        if (isMtdDevice()) {
            LOGI("start flash write to /dev/mtd0.\n");
            size_t total_size;
            size_t erase_size;
            mtd_scan_partitions();
            const MtdPartition *part = mtd_find_partition_by_name("rk-nand");
            if ( part == NULL ) {
                part = mtd_find_partition_by_name("spi-nand0");
            }
            if (part == NULL || mtd_partition_info(part, &total_size, &erase_size, NULL)) {
                if ((!access(FACTORY_FIRMWARE_IMAGE, F_OK)) && mtd_find_partition_by_name("sfc_nor") != NULL) {
                    LOGI("Info: start flash out_image.img to spi nor.\n");
                    system("flashcp -v " FACTORY_FIRMWARE_IMAGE " /dev/mtd0");
                } else
                    LOGE("Error: Can't find rk-nand or spi-nand0.\n");
            } else {
                system("flash_erase /dev/mtd0 0x0 0");
                system("sh "CMD4RECOVERY_FILENAME);
            }
        } else {
            LOGI("Start to dd data to emmc partition.\n");
            system("sh "CMD4RECOVERY_FILENAME);
            LOGI("usb upgrade done\n");
        }
#endif

        if (status == INSTALL_SUCCESS) {
            LOGI("update.img Installation success.\n");
            ui_print("update.img Installation success.\n");
            //ui_show_text(0);
        }
    } else if (wipe_data) {
        if (device_wipe_data()) status = INSTALL_ERROR;
        if (erase_volume("/userdata")) status = INSTALL_ERROR;
        if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
    } else if (wipe_all) {
        if (device_wipe_data()) status = INSTALL_ERROR;
        if (erase_volume("/userdata")) status = INSTALL_ERROR;
        if (status != INSTALL_SUCCESS) {
            ui_print("Data wipe failed.\n");
            LOGE("userdata wipe failed.\n");
        } else {
            ui_print("Data wipe done.\n");
            LOGI("userdata wipe done.\n");
        }
    
        //ui_show_text(0);
    } else if (pcba_test) {
        //pcba test todo...
        printf("------------------ pcba test start -------------\n");
        exit(EXIT_SUCCESS); //exit recovery bin directly, not start pcba here, in rkLanuch.sh
        return 0;
    } else {
        if (argc == 1) { // No command specified
            if (!bSDBootUpdate && !bUdiskUpdate && ui_text_visible())
                prompt_and_wait();
            finish_recovery(NULL);
            reboot(RB_AUTOBOOT);
            return 0;
        }
        status = INSTALL_ERROR;  // No command specified
    }
    
    if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
    if (status != INSTALL_SUCCESS) {
        LOGE("\n Install fail! \n");
        if (!bSDBootUpdate && !bUdiskUpdate && ui_text_visible())
            prompt_and_wait();
    }
    
    if (sdupdate_package != NULL && bSDBootUpdate) {
        if (status == INSTALL_SUCCESS) {
            char *SDDdevice =
                strdup(get_mounted_device_from_path(EX_SDCARD_ROOT));
    
            ensure_ex_path_unmounted(EX_SDCARD_ROOT);
            /* Updating is finished here, we must print this message
             * in console, it shows user a specific message that
             * updating is completely, remove SD CARD and reboot */
            fflush(stdout);
            freopen("/dev/console", "w", stdout);
            LOGI("\nPlease remove SD CARD!!!, wait for reboot.\n");
            ui_print("Please remove SD CARD!!!, wait for reboot.");
    
            while (access(SDDdevice, F_OK) == 0) { sleep(1); }
            free(SDDdevice);
        }
    } else if (usbupdate_package && bUdiskUpdate) {
        if (status == INSTALL_SUCCESS) {
            char *udiskDev = strdup(get_mounted_device_from_path(EX_SDCARD_ROOT));
            ensure_path_unmounted(EX_UDISK_ROOT);
            /* Updating is finished here, we must print this message
             * in console, it shows user a specific message that
             * updating is completely, remove U-disk and reboot */
            fflush(stdout);
            freopen("/dev/console", "w", stdout);
            LOGI("\nPlease remove U DISK!!!, wait for reboot.\n");
            ui_print("Please remove U DISK!!!, wait for reboot.");
    
            while (access(udiskDev, F_OK) == 0) { sleep(1); }
            free(udiskDev);
        }
    }
    
    // Otherwise, get ready to boot the main system...
    finish_recovery(send_intent);
    gettimeofday(&end_time, NULL);
    
    elapsed_time = (end_time.tv_sec - start_time.tv_sec) * 1000LL +
                   (end_time.tv_usec - start_time.tv_usec) / 1000LL;
    LOGI("recovery usage time:%lld ms\n", elapsed_time);
    ui_print("Rebooting...\n");
    LOGI("Reboot...\n");
    ui_show_text(0);
    fflush(stdout);
    sync();
    reboot(RB_AUTOBOOT);
    return EXIT_SUCCESS;
}
2.1 获取命令行参数

首先调用get_args函数,该函数的目的是从多个来源获取命令行参数,并将这些参数存储在 argv 数组中,同时更新misc分区;

// command line args come from, in decreasing precedence:
//   - the actual command line
//   - the bootloader control block (one per line, after "recovery")
//   - the contents of COMMAND_FILE (one per line)
static void
get_args(int *argc, char ***argv)
{
    // 1. misc偏移16k位置存放的是recovery信息,因此recovery系统重启后会加载recovery信息
    struct bootloader_message boot;
    memset(&boot, 0, sizeof(boot));
    get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure

    // 输出命令:Boot command: boot-recovery
    if (boot.command[0] != 0 && boot.command[0] != 255) {
        LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
    }

    // 输出状态
    if (boot.status[0] != 0 && boot.status[0] != 255) {
        LOGI("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
    }

    // --- if arguments weren't supplied, look in the bootloader control block
    // 2. 如果argc小于或等于1(即没有提供命令行参数),函数会尝试从boot.recovery中提取参数
    if (*argc <= 1) {
        // 首先确保boot.recovery字符串以空字符结尾,然后使用strtok将boot.recovery字符串分割成以换行符\n为界的多个部分
        // \n对应的ASCII值是0x0a
        // boot.recovery="recovery\n--update_package=/userdata/update.img\n"
        boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
        // arg=”recovery”
        // boot.recovery="recovery\0--update_package=/userdata/update.img\n"
        const char *arg = strtok(boot.recovery, "\n");
        // 满足条件
        if (arg != NULL && !strcmp(arg, "recovery")) {      
            *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
            // 保存参数arg
            (*argv)[0] = strdup(arg);
            for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
                // 获取下一个子字符串
		        // arg=”--update_package=/userdata/update.img”
        		// boot.recovery="recovery\0--update_package=/userdata/update.img\0"
                if ((arg = strtok(NULL, "\n")) == NULL) break;
                (*argv)[*argc] = strdup(arg);
            }
            LOGI("Got arguments from boot message\n");
        } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
            LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
        }
    }

    // --- if that doesn't work, try the command file
    if (*argc <= 1) {
 		......
    }

    // --> write the arguments we have back into the bootloader control block
    // always boot into recovery after this (until finish_recovery() is called)
    // 3. 设置参数
    // 设置boot.command="boot-recovery"
    // 设置boot.recovery="recovery"
    strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
    strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
    int i;
    // 合并参数,最后boot.recovery="recovery\n--update_package=/userdata/update.img\n"
    for (i = 1; i < *argc; ++i) {
        strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
        strlcat(boot.recovery, "\n", sizeof(boot.recovery));
    }
    //  往misc偏移16k位置写入recovery信息
    set_bootloader_message(&boot);
}

有关struct bootloader_message参考《Rockchip RK3588 - Rockchip Linux Recovery updateEngine源码分析》第4.4节。比如misc分区偏移16k的数据如下:

10000000: 62 6f 6f 74 2d 72 65 63 6f 76 65 72 79 00 00 00    boot-recovery...
10000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000040: 72 65 63 6f 76 65 72 79 0a 2d 2d 75 70 64 61 74    recovery.--updat
10000050: 65 5f 70 61 63 6b 61 67 65 3d 2f 75 73 65 72 64    e_package=/userd
10000060: 61 74 61 2f 75 70 64 61 74 65 2e 69 6d 67 0a 00    ata/update.img..
10000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
10000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
......
10000340: 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
......
2.2 解析命令行参数

由于recovery是通过传参来实现固件升级的,因此不难猜出recovery是基于命令行参数的程序。

接着使用了getopt_long函数来解析命令行参数,并根据参数的不同执行相应的操作。

static const struct option OPTIONS[] = {
    { "send_intent", required_argument, NULL, 's' },
    { "update_package", required_argument, NULL, 'u' },
    { "wipe_data", no_argument, NULL, 'w' },
    { "wipe_all", no_argument, NULL, 'a' },
    { "set_encrypted_filesystems", required_argument, NULL, 'e' },
    { "show_text", no_argument, NULL, 't' },
    { "factory_pcba_test", no_argument, NULL, 'f' },
    { "rkdebug", no_argument, NULL, 'r'},
    { "ui_rotation", required_argument, NULL, 'i'},
    { NULL, 0, NULL, 0 },
};

while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
	switch (arg) {
	case 'p':
		previous_runs = atoi(optarg);
		break;
	case 's':
		send_intent = optarg;
		break;
	case 'u':  // --update_package=/userdata/update.img
		update_package = optarg;
		break;
	case 'w':
		wipe_data = 1;
		break;
	case 'a':
		wipe_all = 1;
		break;
	case 'e':
		encrypted_fs_mode = optarg;
		toggle_secure_fs = 1;
		break;
	case 't':
		ui_show_text(1);
		break;
	case 'f':
		pcba_test = 1;
		break;  // add for pcba test
	case 'r':
		isrkdebug = true;
		break;
	case 'i':
		gr_set_rotate(atoi(optarg));
		break;
	case '?':
		LOGE("Invalid command argument\n");
		continue;
	}
}

getopt_long 这是一个用于获取命令行选项的函数。它可以处理长选项,并与短选项配合使用。

  • argcargv是标准的命令行参数;
  • OPTIONS是定义选项的字符串或结构体(通常包含短选项及其参数信息);
  • NULL 是指不使用长选项的返回值。

由于(*argv)[0]="recovery"(*argv)[1]="--update_package=/userdata/update.img",因此会进入u分支,设置update_package="/userdata/update.img"

2.3 输出版本信息

接着main函数输出当前recovery版本信息,比如:

*********************************************************
            ROCKCHIP recovery system
*********************************************************
**** version : V1.0.1-g28f720bc5-240524-dirty ****
LOG_INFO: Starting recovery on Sun Oct  6 09:04:09 2024

如果没有配置RecoveryNoUi,输出:

LOG_INFO: Recovery System have UI defined.
2.4 ui相关
2.4.1 ui_init

ui_init函数定义在ui.c文件;

void ui_init(void)
{
    int ret = 0;
    ret = gr_init();
    if (ret)
        return;

    ev_init((ev_callback)input_callback, NULL);

    text_col = text_row = 0;
    text_rows = gr_fb_height() / CHAR_HEIGHT;
    if (text_rows > MAX_ROWS) text_rows = MAX_ROWS;
    text_top = 1;

    text_cols = gr_fb_width() / CHAR_WIDTH;
    if (text_cols > MAX_COLS - 1) text_cols = MAX_COLS - 1;

    int i;
    for (i = 0; BITMAPS[i].name != NULL; ++i) {
        int result = res_create_display_surface(BITMAPS[i].name, BITMAPS[i].surface);
        if (result < 0) {
            if (result == -2) {
                LOGI("Bitmap %s missing header\n", BITMAPS[i].name);
            } else {
                LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result);
            }
            *BITMAPS[i].surface = NULL;
        }
    }

    pthread_t t;
    pthread_create(&t, NULL, progress_thread, NULL);
    pthread_create(&t, NULL, input_thread, NULL);
}
2.4.2 ui_set_background

ui_set_background函数定义在ui.c文件;

void ui_set_background(int icon)
{
    if (!gr_draw)
        return;

    pthread_mutex_lock(&gUpdateMutex);
    gCurrentIcon = gBackgroundIcon[icon];
    update_screen_locked();
    pthread_mutex_unlock(&gUpdateMutex);
}
2.5 加载/etc/fstab

load_volume_table函数用于输出/etc/fstab中定义文件系统的挂载点信息:

rk3399 login: recovery filesystem table
=========================
  0 (null) /tmp ramdisk (null) (null) (null)
  1 /dev/root / ext2 rw,noauto 0 1
  2 proc /proc proc defaults 0 0
  3 devpts /dev/pts devpts defaults,gid=5,mode=620,ptmxmode=0666 0 0
  4 tmpfs /dev/shm tmpfs mode=1777 0 0
  5 tmpfs /tmp tmpfs mode=1777 0 0
  6 tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0
  7 sysfs /sys sysfs defaults 0 0
  8 configfs /sys/kernel/config configfs defaults 0 0
  9 debugfs /sys/kernel/debug debugfs defaults 0 0

load_volume_table函数定义在roots.c

void load_volume_table()
{
    int alloc = 2;
    device_volumes = malloc(alloc * sizeof(Volume));

    // Insert an entry for /tmp, which is the ramdisk and is always mounted.
    device_volumes[0].mount_point = "/tmp";
    device_volumes[0].fs_type = "ramdisk";
    device_volumes[0].device = NULL;
    device_volumes[0].device2 = NULL;
    device_volumes[0].option = NULL;
    device_volumes[0].dump = NULL;
    device_volumes[0].pass = NULL;
    num_volumes = 1;

    FILE* fstab = fopen("/etc/fstab", "r");
    if (fstab == NULL) {
        LOGE("failed to open /etc/fstab (%d)\n", errno);
        return;
    }

    char buffer[1024];
    char file_system[1024];
    char mount_point[1024];
    char fs_type[1024];
    char option[1024];
    char dump[1024];
    char pass[1024];
    char device[1024];
    int i;
    while (fgets(buffer, sizeof(buffer) - 1, fstab)) {
        i = sscanf(buffer, "%s %s %s %s %s %s", file_system,
                   mount_point, fs_type, option, dump, pass);
        if (file_system[0] == '#') continue;
        //printf("load_volume_table file_system:%s, mount_point:%s, fs_type:%s, option:%s, dump:%s, pass:%s\n", file_system, mount_point, fs_type, option, dump, pass);
        /* HACK: Convert *LABEL to "by-name" symlink */
        if (strstr(file_system, "LABEL="))
            snprintf(device, sizeof(device), "/dev/block/by-name/%s",
                     strstr(file_system, "LABEL=") + strlen("LABEL="));
        else
            strcpy(device, file_system);

        if (i == 6) {
            while (num_volumes >= alloc) {
                alloc *= 2;
                device_volumes = realloc(device_volumes, alloc * sizeof(Volume));
            }
            device_volumes[num_volumes].mount_point = strdup(mount_point);
            device_volumes[num_volumes].fs_type = strdup(fs_type);
            device_volumes[num_volumes].option = strdup(option);
            device_volumes[num_volumes].dump = strdup(dump);
            device_volumes[num_volumes].pass = strdup(pass);
            device_volumes[num_volumes].device = strdup(device);;
            device_volumes[num_volumes].device2 = NULL;
            ++num_volumes;
        } else {
            LOGE("skipping malformed recovery.fstab line: %s\n", buffer);
        }
    }

    fclose(fstab);

    printf("recovery filesystem table\n");
    printf("=========================\n");
    for (i = 0; i < num_volumes; ++i) {
        Volume* v = &device_volumes[i];
        printf("  %d %s %s %s %s %s %s\n", i, v->device, v->mount_point, v->fs_type, v->option, v->dump, v->pass);
    }
    printf("\n");
}
2.6 setFlashPoint

setFlashPoint函数定义在rktools.c

void setFlashPoint()
{
    // 判断是不是MTD设备(Nor/Nand Flash设备),对于SD、eMMC不属于MTD设备
    if (!isMtdDevice())   // 进入
        // 等待设备节点/dev/block/by-name/misc可用
        wait_for_device(MISC_PARTITION_NAME_BLOCK);

    // 通过读取/sys/bus/mmc/devices/目录中的条目来查找SD或eMMC设备,并更新result_point
    init_sd_emmc_point();
    
    // 设置环境变量emmc_point_name="/dev/mmcblk2"
    setenv(EMMC_POINT_NAME, result_point[MMC], 1);    
    
    // 判断SD类型设备是否存在,设备节点为/dev/mmcblk0
    if (access(result_point[SD], F_OK) == 0)
        // 设置环境变量sd_point_name_2
        setenv(SD_POINT_NAME_2, result_point[SD], 1);
    
    // /dev/mmcblk0p1
    char name_t[22];
    if (strlen(result_point[SD]) > 0) {        
        strcpy(name_t, result_point[SD]);
        strcat(name_t, "p1");
    }
    if (access(name_t, F_OK) == 0)
        // 设置环境变量sd_point_name
        setenv(SD_POINT_NAME, name_t, 1);

    // emmc_point_name
    LOGI("emmc_point is %s\n", getenv(EMMC_POINT_NAME));
    // sd_point_name
    LOGI("sd_point is %s\n", getenv(SD_POINT_NAME));
    // sd_point_name_2
    LOGI("sd_point_2 is %s\n", getenv(SD_POINT_NAME_2));
}

函数执行完,输出信息大致如下;

LOG_INFO: devices is not MTD.
LOG_INFO: emmc_point is /dev/mmcblk2
LOG_INFO: sd_point is (null)
LOG_INFO: sd_point_2 is (null)
2.6.1 wait_for_device

wait_for_device用于 确保某个文件(例如设备节点,挂载点或配置文件)可用;

static void wait_for_device(const char* fn)
{
    int tries = 0;
    int ret;
    struct stat buf;
    do {
        ++tries;
        // 检查文件状态
        ret = stat(fn, &buf);
        // 回非零值,表示路径不可用	
        if (ret) {
            LOGI("stat %s try %d: %s\n", fn, tries, strerror(errno));
            sleep(1);
        }
    } while (ret && tries < 10);
    if (ret) {
        LOGI("failed to stat %s\n", fn);
    }
}
2.6.2 init_sd_emmc_point

init_sd_emmc_point通过读取/sys/bus/mmc/devices/目录中的条目来查找SDeMMC设备,并更新result_point

static const char *point_items[] = {
    "/dev/mmcblk0",     // MMC类型设备对应的设备节点
    "/dev/mmcblk1",     // SD类型设备对应的设备节点
    "/dev/mmcblk2",     // SDIO类型设备对应的设备节点
    "/dev/mmcblk3",     // SDcombo类型设备对应的设备节点
};

enum type {
    MMC,            // MMC类型
    SD,             // SD类型
    SDIO,           // SDIO类型
    SDcombo,        // .....
};

static const char *typeName[] = {
    "MMC",
    "SD",
    "SDIO",
    "SDcombo",
};

/**
 *  设置flash 节点
 */
static char result_point[4][20] = {'\0'}; //0-->MMC, 1-->SD, 2-->SDIO, 3-->SDcombo

// 读取dir目录下的filename文件
int readFile(DIR* dir, char* filename)
{
    char name[30] = {'\0'};
    int i;

    // 构建文件路径:mmc0:0001/type
    // 构建文件路径:mmc1:0001/type
    // 构建文件路径:mmc2:0001/type
    strcpy(name, filename);
    strcat(name, "/type");
    
    // 打开文件
    int fd = openat(dirfd(dir), name, O_RDONLY);
    if (fd == -1) {
        LOGE("Error: openat %s error %s.\n", name, strerror(errno));
        return -1;
    }
    
    // 读取文件内容到resultBuf中
    char resultBuf[10] = {'\0'};
    if (read(fd, resultBuf, sizeof(resultBuf)) < 1) {
        return -1;
    }
    
    // 将换行符替换为字符串结束符
    for (i = 0; i < strlen(resultBuf); i++) {
        if (resultBuf[i] == '\n') {
            resultBuf[i] = '\0';
            break;
        }
    }
    
    // 比较读取的内容与预定义的类型名称typeName,如果匹配则返回索引
    for (i = 0; i < 4; i++) {
        if (strcmp(typeName[i], resultBuf) == 0) {
            //printf("type is %s.\n", typeName[i]);
            return i;
        }
    }

    LOGE("Error:no found type!\n");
    return -1;
}

// 通过读取/sys/bus/mmc/devices/目录中的条目来查找SD或eMMC设备
void init_sd_emmc_point()
{
    DIR* dir = opendir("/sys/bus/mmc/devices/");
    // 目录存在
    if (dir != NULL) {
        struct dirent* de;
        // 循环读取目录中的每个条目,比如mmc0:0001、mmc1:0001、mmc2:0001
        while ((de = readdir(dir))) {
            // 排除当前目录 (.) 和父目录 (..)
            if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0 )
                continue;
            //if (de->d_type == 4)    //dir
            //    printf("dir name : %s \n", de->d_name);

            // 检查目录项名称是否以mmc开头,以确定是否是mmc设备。	
            if (strncmp(de->d_name, "mmc", 3) == 0) {
                //printf("find mmc is %s.\n", de->d_name);
                char flag = de->d_name[3];
                int ret = -1;
                // 读取dir目录下的mmc0:0001/type文件,获取设备类型
                ret = readFile(dir, de->d_name);
                // 设备类型存在
                if (ret != -1) {
                    // mmc0:0001/type -> result_point[SDIO]="/dev/mmcblk2"
                    // mmc1:0001/type -> result_point[SD]="/dev/mmcblk1"
                    // mmc2:0001/type -> result_point[MMC]="/dev/mmcblk0"
                    strcpy(result_point[ret], point_items[flag - '0']);
                } else {
                    // 无效
                    strcpy(result_point[ret], "");
                }
            }
        }
    }
    closedir(dir);
}

NanoPC-T4开发板为例(未接入SD卡):

pi@NanoPC-T4:~$ ls -l  /sys/bus/mmc/devices
total 0
lrwxrwxrwx 1 root root 0 Oct  6 14:41 mmc0:0001 -> ../../../devices/platform/fe310000.mmc/mmc_host/mmc0/mmc0:0001
lrwxrwxrwx 1 root root 0 Oct  6 14:41 mmc2:0001 -> ../../../devices/platform/fe330000.sdhci/mmc_host/mmc2/mmc2:0001
pi@NanoPC-T4:~$ cat  /sys/bus/mmc/devices/mmc0\:0001/type
SDIO
pi@NanoPC-T4:~$ cat  /sys/bus/mmc/devices/mmc2\:0001/type
MMC

函数执行完毕:

result_point[MMC]="/dev/mmcblk0"
result_point[SD]=""
result_point[SDIO]="/dev/mmcblk2"
result_point[SDcombo]=""

如果插入SD卡:

pi@NanoPC-T4:~$ ls -l  /sys/bus/mmc/devices
total 0
lrwxrwxrwx 1 root root 0 Oct  6 14:41 mmc0:0001 -> ../../../devices/platform/fe310000.mmc/mmc_host/mmc0/mmc0:0001
lrwxrwxrwx 1 root root 0 Oct  6 15:02 mmc1:0001 -> ../../../devices/platform/fe320000.mmc/mmc_host/mmc1/mmc1:0001
lrwxrwxrwx 1 root root 0 Oct  6 14:41 mmc2:0001 -> ../../../devices/platform/fe330000.sdhci/mmc_host/mmc2/mmc2:0001

pi@NanoPC-T4:~$ cat  /sys/bus/mmc/devices/mmc1\:0001/type
SD
2.7 启动方式判定

接着通过读取命令行信息判断启动方式,用于检测是否从SD/USB启动;

bSDBoot = is_boot_from_SD();
bUDiskBoot = is_boot_from_udisk();

if (bSDBoot || bUDiskBoot) {
	char imageFile[64] = {0};
	if (bSDBoot) {
		if (is_sdcard_update()) {
			strlcpy(imageFile, EX_SDCARD_ROOT, sizeof(imageFile));
			strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));
			if (access(imageFile, F_OK) == 0) {
				sdupdate_package = strdup(imageFile);
				bSDBootUpdate = true;
				ui_show_text(1);
				LOGI("sdupdate_package = %s\n", sdupdate_package);
			}
		}
	}

	if (bUDiskBoot) {
		if (is_udisk_update()) {
			strlcpy(imageFile, EX_UDISK_ROOT, sizeof(imageFile));
			strlcat(imageFile, "/sdupdate.img", sizeof(imageFile));
			if (access(imageFile, F_OK) == 0) {
				usbupdate_package = strdup(imageFile);
				bUdiskUpdate = true;
				ui_show_text(1);
				LOGI("usbupdate_package = %s\n", usbupdate_package);
			}
		}
	}
}

由于我们采用的eMMC启动方式,所以输出如下日志:

LOG_INFO: read cmdline
LOG_INFO: >>> Boot from non-SDcard
LOG_INFO: read cmdline
LOG_INFO: >>> Boot from non-U-Disk
2.7.1 is_boot_from_SD

is_boot_from_SD函数定义在sdboot.c

bool is_boot_from_SD(void)
{
    bool bSDBoot = false;
    char param[1024];
    int fd, ret;
    char *s = NULL;
    LOGI("read cmdline\n");
    memset(param, 0, 1024);

    // 读取命令行
    fd = open("/proc/cmdline", O_RDONLY);
    ret = read(fd, (char*)param, 1024);

    // 查找sdfwupdate字符串
    s = strstr(param, "sdfwupdate");
    if (s != NULL) {
        bSDBoot = true;
        LOGI(">>> Boot from SDcard\n");
    } else {
        bSDBoot = false;
        LOGI(">>> Boot from non-SDcard\n");
    }

    close(fd);
    return bSDBoot;
}
2.7.2 is_boot_from_udisk

is_boot_from_udisk函数定义在usbboot.c

bool is_boot_from_udisk(void)
{
    bool bUDisk = false;
    char param[1024];
    int fd, ret;
    char *s = NULL;
    LOGI("read cmdline\n");
    memset(param, 0, 1024);

    // 读取命令行
    fd = open("/proc/cmdline", O_RDONLY);
    ret = read(fd, (char*)param, 1024);

    // 查找usbfwupdate字符串
    s = strstr(param, "usbfwupdate");
    if (s != NULL) {
        bUDisk = true;
        LOGI(">>> Boot from U-Disk\n");
    } else {
        bUDisk = false;
        LOGI(">>> Boot from non-U-Disk\n");
    }

    close(fd);
    return bUDisk;
}

参考文章:

[1] Rockchip RK3588 - Rockchip Linux Recovery updateEngine源码分析

[2] Rockchip RK3588 - Rockchip Linux Recovery updateEngine测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graceful_scenery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值