2021-05-12 14:32:11
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, "%dn",
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 %08xn", *((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燒寫或者進行重要資料更新。
相關文章