zoukankan      html  css  js  c++  java
  • Linux和Uboot下eMMC boot分区读写

     关键词: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
    ",
                   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
    ", *((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烧写或者进行重要数据更新。

  • 相关阅读:
    XML案例
    4.6Java数组的遍历
    4.3Java多态(polymorphism)
    XML文档的标准
    4.6Java数组的定义
    4.6Java数组初始化的方式
    XML介绍
    4.6Java对象转型(casting)
    HelloWorld之MyBatis
    Hibernate查询方法比较
  • 原文地址:https://www.cnblogs.com/arnoldlu/p/10762009.html
Copyright © 2011-2022 走看看