Linux和Uboot下eMMC boot分区读写

Stella981
• 阅读 1452

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

1. eMMC的分区

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

Linux和Uboot下eMMC boot分区读写

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。

Linux和Uboot下eMMC boot分区读写

Linux和Uboot下eMMC boot分区读写

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烧写或者进行重要数据更新。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这