前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux和Uboot下eMMC boot分区读写

Linux和Uboot下eMMC boot分区读写

作者头像
知忆
修改2021-06-11 10:01:46
5.9K0
修改2021-06-11 10:01:46
举报
文章被收录于专栏:linux百科小宇宙

关键词:eMMC boot、PARTITION_CONFIG、force_ro等。

1. eMMC的分区

大部分eMMC都有类似如下的分区,其中BOOT、RPMB和UDA一般是默认存在的,gpp分区需要手动创建。

BOOT主要是为了支持从eMMC启动系统而设计的;RPMB即Replay Protected Memory Block简称,通常用来保存安全线管的数据;GPP主要用于存储系统或者用户数据。

UDA通常会进行再分区,然后根据不同目的存放相关数据,或者格式化成不同文件系统。

2. Linux下读写boot分区

因为boot分区中一般存放的是bootloader或者相关配置参数,这些参数一般是不允许修改的,所以默认情况下是能读boot分区,不能写。

2.1 使能读写

如果需要些则需要,修改/sys/block/mmcblk0boot1/force_ro。

使能写:

echo 0 > /sys/block/mmcblk0boot1/force_ro

关闭写:

echo 1 > /sys/block/mmcblk0boot1/force_ro

在重启之后,force_ro会恢复为1。

2.2 内核force_ro实现

下面来看看force_ro是如何起作用的?

eMMC在被初始化的时候,调用mmc_blk_probe(),这里面会在每个设备下创建force_ro sysfs节点。

static int mmc_blk_probe(struct mmc_card *card)

{

...

    if (mmc_add_disk(md))

        goto out;

...

}

static int mmc_add_disk(struct mmc_blk_data *md)

{

    int ret;

    struct mmc_card *card = md->queue.card;

    device_add_disk(md->parent, md->disk);

    md->force_ro.show = force_ro_show;

    md->force_ro.store = force_ro_store;----------------------------------------------设置分区是否只读,0可读写;1只读。

    sysfs_attr_init(&md->force_ro.attr);

    md->force_ro.attr.name = "force_ro";

    md->force_ro.attr.mode = S_IRUGO | S_IWUSR;

    ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);

    if (ret)

        goto force_ro_fail;

    if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&

        card->ext_csd.boot_ro_lockable) {

        umode_t mode;

        if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)

            mode = S_IRUGO;

        else

            mode = S_IRUGO | S_IWUSR;

        md->power_ro_lock.show = power_ro_lock_show;

        md->power_ro_lock.store = power_ro_lock_store;

        sysfs_attr_init(&md->power_ro_lock.attr);

        md->power_ro_lock.attr.mode = mode;

        md->power_ro_lock.attr.name =

                    "ro_lock_until_next_power_on";

        ret = device_create_file(disk_to_dev(md->disk),

                &md->power_ro_lock);

        if (ret)

            goto power_ro_lock_fail;

    }

    return ret;

power_ro_lock_fail:

    device_remove_file(disk_to_dev(md->disk), &md->force_ro);

force_ro_fail:

    del_gendisk(md->disk);

    return ret;

}

static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,

                char *buf)

{

    int ret;

    struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));

    ret = snprintf(buf, PAGE_SIZE, "%d\n",

              get_disk_ro(dev_to_disk(dev)) ^

              md->read_only);

    mmc_blk_put(md);

    return ret;

}

static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr,

                  const char *buf, size_t count)

{

    int ret;

    char *end;

    struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));

    unsigned long set = simple_strtoul(buf, &end, 0);

    if (end == buf) {

        ret = -EINVAL;

        goto out;

    }

    set_disk_ro(dev_to_disk(dev), set || md->read_only);

    ret = count;

out:

    mmc_blk_put(md);

    return ret;

}

2.3 读写boot分区操作

在force_ro为1的情况下,写boot分区返回Operation not permitted。

echo updt | dd of=/dev/mmcblk0boot1 bs=4 count=1 seek=0 && sync

dd: writing '/dev/mmcblk0boot1': Operation not permitted

1+0 records in

0+0 records out

然后打开force_ro=0:

echo 0 > /sys/block/mmcblk0boot1/force_ro && echo updt | dd of=/dev/mmcblk0boot1 bs=4 count=1 seek=0 && sync

通过hexdump验证一下:

hexdump -v -n 4 -s 0 /dev/mmcblk0boot1

0000000 7075 7464                             

0000004

3. uboot下读写boot分区

uboot下操作boot分区需要打开CONFIG_SUPPORT_EMMC_BOOT。

在Linux下/dev/mmcblk0boot1就表示切换到boot分区了,在uboot下需要先切换到boot分区。

3.1 PARTITION_CONFIG寄存器

由于默认分区是UDA,而eMMC每个分区都是独立编址的。所以要使用boot分区需要切换分区。

PARTITION_CONFIG寄存器,通过EXT_CSD_PART_CONF命令来设置。

根据下面的寄存解释,BOOT_ACK设置为0x0,;BOOT_PARTITION_ENABLE设置为0x2;PARTITION_ACCESS设置为0x2。

3.2 读取boot分区

uboot中读取boot分区,首先需要将分区切换到boot分区,然后读写分区,最后将分区切换回原来分区。

static int do_mmc_bootmode(cmd_tbl_t *cmdtp, int flag,

            int argc, char * const argv[])

{

    struct mmc *mmc;

    int ret = BOOTMODE_NORMAL;

    u32 blk, cnt, n;

    void *addr;

    char original_part;

    addr = (void *)malloc(512);

    blk = BOOTMODE_BLK_NUM;

    cnt = BOOTMODE_BLK_COUNT;

    mmc = init_mmc_device(curr_device, false);

    if (!mmc)

    {

        free(addr);

        return CMD_RET_FAILURE;

    }

    /* Switch to the Boot 2 partition */

    original_part = mmc->block_dev.hwpart;

    blk_select_hwpart_devnum(IF_TYPE_MMC, curr_device, MMC_PART_BOOT2);

    mmc_set_part_conf(mmc, 0, MMC_PART_BOOT2, 2);------------------------------------------切换到eMMC boot1分区。

    n = blk_dread(mmc_get_blk_desc(mmc), blk, cnt, addr);----------------------------------读取一个block。

    if(n != cnt)

    {

        free(addr);

        return CMD_RET_FAILURE;

    }

    /* flush cache after read */

    flush_cache((ulong)addr, cnt * 512); /* FIXME */

    if(*(unsigned int *)addr == BOOTMODE_UPDATE_MAGIC)

    {

        ret = BOOTMODE_UPDATE;

    }

    else

    {

        ret = BOOTMODE_NORMAL;

    }

#if 0

    for(int i = 0; i < 512/16; i++)

        printf("%08x %08x %08x %08x\n", *((int *)addr+i*4), *((int *)addr+i*4+1), *((int *)addr+i*4+2), *((int *)addr+i*4+3));

#endif

    /* Switch to original partition. */

    blk_select_hwpart_devnum(IF_TYPE_MMC, curr_device, original_part);----------------------切换到默认分区。

    free(addr);

    return ret;

}

至此可以在Linux和Uboot下对boot分区进行操作,进行bootloader烧写或者进行重要数据更新。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
命令行工具
腾讯云命令行工具 TCCLI 是管理腾讯云资源的统一工具。使用腾讯云命令行工具,您可以快速调用腾讯云 API 来管理您的腾讯云资源。此外,您还可以基于腾讯云的命令行工具来做自动化和脚本处理,以更多样的方式进行组合和重用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档