quinnwencn / blog

Apache License 2.0
0 stars 0 forks source link

09 基于u-boot命令的ostree环境变量设置 #26

Open quinnwencn opened 2 months ago

quinnwencn commented 2 months ago

背景

前一个话题todo讨论了如果在板子上启用secure boot,基于OSTree的升级方案会因为无法设置环境变量而无法启动,也给出了一个新增分区用于记录环境变量的方案。但是,从更合理的角度,我们不应该引入任何的分区和文件记录启动信息。由于U-BOOT具有基本的目录检索能力,我们可以基于U-BOOT的这个能力,制作一个U-BOOT命令,用以实现获取rootfs启动的参数。

基本思路

基于ostree的rootfs的启动目录如下:

/sysroot/
|-- boot
|-- boot.cfg
|-- dev
|-- home
|-- lost+found
|-- ostree
    |-- boot.0 -> boot.0.1
        `-- tcu
            `-- 7998bb342a92ae7def2a55e6c40fbc5dbbbde41b7421a5258f61a4ccbeb0b4d6
                 |-- 0
                 `-- 1
    |-- boot.0.1
    |-- deploy
    `-- repo
|-- proc
|-- root
|-- run
|-- sys
`-- tmp

启动相关的目录是:/sysroot/ostree/boot.0/7998bb342a92ae7def2a55e6c40fbc5dbbbde41b7421a5258f61a4ccbeb0b4d6/0,其中在启动时,变化的只有boot.0中的0, 哈希值以及最后的0或者1。因此,我们可以在uboot中遍历查找当前的值,并存储起来添加到bootargs环境变量的末尾即可。

实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <common.h>
#include <command.h>
#include <fs.h>
#include <part.h>
#include <linux/ctype.h>
#include <linux/stddef.h>

#define OSTREE "ostree"
#define BOOT_FLAG0 "boot.0"
#define BOOT_FLAG1 "boot.1"
#define ROLLBACK_FLAG "0"
#define HASH_SIZE 64
#define OLD_BOOTARGS "old_bootargs"

static int update_bootargs_env_with_ostree_env(const char* ostree_env) {
    char *old_env = NULL;
    char new_env[1000];

    old_env = env_get(OLD_BOOTARGS);
    if (old_env == NULL) {
        old_env = env_get("bootargs");
        if (old_env == NULL) {
            printf("[ERR] error bootargs read!\n");
            return 1;
        }
        env_set(OLD_BOOTARGS, old_env);
    }

    sprintf(new_env, "%s %s", old_env, ostree_env);
    env_set("bootargs", new_env);

    return 0;
}

int ostree_boot(int is_rollback) {
    struct fs_dir_stream *dirs = NULL;
    struct fs_dirent *dent = NULL;
    char boot_dir[100];

    dirs = fs_opendir(OSTREE);
    if (dirs == NULL) {
        printf("[ERROR] %s not found\n", OSTREE);
        return 1;
    }

    memset(boot_dir, 0, sizeof(boot_dir));
    while ((dent = fs_readdir(dirs))) {
        if (dent->type == FS_DT_DIR && strcmp(dent->name, BOOT_FLAG0) == 0) {
            sprintf(boot_dir, "ostree/%s/tcu", BOOT_FLAG0);
        } else if (dent->type == FS_DT_DIR && strcmp(dent->name, BOOT_FLAG1) == 0) {
            sprintf(boot_dir, "ostree/%s/tcu", BOOT_FLAG1);
        } else {
            continue;
        }
    }

    fs_closedir(dirs);
    if (boot_dir[0] == '\0') {
        printf("[ERROR] Boot path not found\n");
        return 1;
    }

    dirs = fs_opendir(boot_dir);
    if (dirs == NULL) {
        printf("[ERROR] %s not found\n", boot_dir);
        return 1;
    }

    char hash[HASH_SIZE + 1];
    memset(hash, 0, sizeof(hash));
    while ((dent = fs_readdir(dirs))) {
        if (dent->type == FS_DT_DIR || strlen(dent->name)== HASH_SIZE) {
            memcpy(hash, dent->name, HASH_SIZE);
        }
    }

    if (hash[0] == '\0') {
        fs_closedir(dirs);
        printf("[ERROR] Hash not found\n");
        return 1;
    }

    fs_closedir(dirs);
    char boot_str[500];
    memset(boot_str, 0, sizeof(boot_str));
    sprintf(boot_str, "ostree=%s/%s/%u, ostree_root=/dev/mmcblk0p4", boot_dir, hash, is_rollback ? 1 : 0);

    if (update_bootargs_env_with_ostree_env(boot_str)) {
        printf("[ERROR] Failed to update bootargs\n");
        return 1;
    }

    return 0;
}

int do_ostree_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) {
    if (argc != 3) {
        printf("Usage: ostree_boot ifname <device>:<partition>\n");
        return 1;
    }

    if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_EXT)) {
        return 1;
    }

    if (ostree_boot(0)) {
        return 1;
    }
}

int do_ostree_rollback(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) {
    if (argc != 3) {
        printf("Usage: ostree_rollback ifname <device>:<partition>\n");
        return 1;
    }

    if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_EXT)) {
        return 1;
    }

    if (ostree_boot(1)) {
        return 1;
    }
}

U_BOOT_CMD(
    ostree_rollback,
    3,
    1,
    do_ostree_rollback,
    "format : do_ostree_rollback <dev>:<part> <is_rollback>",
    "example: do_ostree_rollback 0:1 0"
);

U_BOOT_CMD(
    ostree_boot,
    3,
    1,
    do_ostree_boot,
    "format: ostree_boot <ifname> <dev>:<part>",
    "example: ostree_boot mmc 0:4"
)

在uboot/common中新建文件ostree_env.c,并在同目录的Makefile下添加:obj-y += ostree_env.o,编译后即可测试。

quinnwencn commented 2 months ago

对于EXT4格式的分区,测试失败,原因是:

#if CONFIG_IS_ENABLED(FS_EXT4)
    {
        .fstype = FS_TYPE_EXT,
        .name = "ext4",
        .null_dev_desc_ok = false,
        .probe = ext4fs_probe,
        .close = ext4fs_close,
        .ls = ext4fs_ls,
        .exists = ext4fs_exists,
        .size = ext4fs_size,
        .read = ext4_read_file,
#ifdef CONFIG_CMD_EXT4_WRITE
        .write = ext4_write_file,
        .ln = ext4fs_create_link,
#else
        .write = fs_write_unsupported,
        .ln = fs_ln_unsupported,
#endif
        .uuid = ext4fs_uuid,
        .opendir = fs_opendir_unsupported,
        .unlink = fs_unlink_unsupported,
        .mkdir = fs_mkdir_unsupported,
    },

ext4不支持fs_opendir。 TODO