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\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燒寫或者進行重要數據更新。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM