關鍵詞:eMMC boot、PARTITION_CONFIG、force_ro等。安全
大部分eMMC都有相似以下的分區,其中BOOT、RPMB和UDA通常是默認存在的,gpp分區須要手動建立。spa
BOOT主要是爲了支持從eMMC啓動系統而設計的;RPMB即Replay Protected Memory Block簡稱,一般用來保存安全線管的數據;GPP主要用於存儲系統或者用戶數據。.net
UDA一般會進行再分區,而後根據不一樣目的存放相關數據,或者格式化成不一樣文件系統。設計
由於boot分區中通常存放的是bootloader或者相關配置參數,這些參數通常是不容許修改的,因此默認狀況下是能讀boot分區,不能寫。code
若是須要些則須要,修改/sys/block/mmcblk0boot1/force_ro。blog
使能寫:rem
echo 0 > /sys/block/mmcblk0boot1/force_ro
關閉寫:get
echo 1 > /sys/block/mmcblk0boot1/force_ro
在重啓以後,force_ro會恢復爲1。cmd
下面來看看force_ro是如何起做用的?it
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; }
在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
uboot下操做boot分區須要打開CONFIG_SUPPORT_EMMC_BOOT。
在Linux下/dev/mmcblk0boot1就表示切換到boot分區了,在uboot下須要先切換到boot分區。
因爲默認分區是UDA,而eMMC每一個分區都是獨立編址的。因此要使用boot分區須要切換分區。
PARTITION_CONFIG寄存器,經過EXT_CSD_PART_CONF命令來設置。
根據下面的寄存解釋,BOOT_ACK設置爲0x0,;BOOT_PARTITION_ENABLE設置爲0x2;PARTITION_ACCESS設置爲0x2。
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燒寫或者進行重要數據更新。