uboot/Linux下MMC/SD/SDIO阅读记录
1 uboot下MMC/SD/SDIO
1.1 uboot下MMC/SD/SDIO相关配置
uboot下关于MMC/SD/SDIO驱动以及工具配置:
->MMC Host controller Support
MMC/SD/SDIO card support
support for MMC/SD write operations--支持对MMC/SD/SD Card的写访问。
Poll for broken card detection case
Enable MMC controllers using Driver Model
Enable quirks
Support for HW partitioning command(eMMC)
Support eMMC replay protected memory block (RPMB)--支持对RPMB分区的读写和秘钥烧录。
Support some additional features of the eMMC boot partitions--支持对boot分区的操作。
Support IO voltage configuration--支持UHS模式的IO电压调整。
enable HS400 Enhanced Strobe support--支持HS400 Ehanced Strobe模式,支持高达200MHz总线。不需要对IO调优。
enable HS400 support--支持HS400模式,高达200MHz总线。需要对IO调优。
enable HS200 support--支持HS200模式,高达200MHz总线。需要对IO调优。
Output more information about the MMC
MMC debugging
Secure Digital Host Controller Interface support
Command line interface
->Device access commands
->mmc
1.2 uboot下MMC/SD/SDIO代码
uboot下SD相关代码有:
drivers/mmc/ ├── mmc_boot.c--mmc boot分区调整、以及分区设置等。 ├── mmc.c--MMC/SD的主要API实现,包括初始化、协议处理等。 ├── mmc-uclass.c--mmc_blk驱动。 ├── mmc_write.c--实现mmc_berase/mmc_bwrite等函数。 ├── sdhci.c--针对MMC/SD主机控制器API实现。 └── zynq_sdhci.c--arasan_sdhci驱动。
1.3 uboot下MMC/SD/SDIO工具
mmcinfo
查看当前MMC设备信息,包括名称、速度、容量、位宽等等。
Device: mmc@04020000 Manufacturer ID: ad OEM: 4c53 Name: LX32G Bus Speed: 50000000 Mode: SD High Speed (50MHz) Rd Block Len: 512 SD version 3.0 High Capacity: Yes Capacity: 29.5 GiB Bus Width: 4-bit Erase Group Size: 512 Bytes
mmc
mmc用于查看MMC/SD设备信息、读写擦除、分区等。
mmc info - 显示MMC/SD设备信息,等同于mmcinfo。 mmc read addr blk# cnt--从blk#开始读取cnt个sector到addr。 mmc write addr blk# cnt mmc erase blk# cnt mmc rescan mmc part - 显示当前MMC/SD设备的分区表。 mmc dev [dev] [part] - show or set current mmc device [partition] mmc list - 显示当前可用MMC/SD设备。
ext4ls/ext4load/ext4write/ext4size
uboot中提供了ext4文件系统的列表、读、写等命令:
ext4load - 从ext4文件系统加载文件到内存。 ext4ls - 显示ext4文件列表。 ext4size - 查看ext4文件大小。
ext4write - 在ext4文件系统中创建一个文件。
以ext4load为例,将/bin/busybox加载到0x80000000:
ext4load mmc 0:e 0x85000000 /bin/busybox--读取mmc0的第0xe个分区的ext4中的/bin/busybox文件。 861848 bytes read in 61 ms (13.5 MiB/s)
ext4load <interface> [<dev[:part]> [addr [filename [bytes [pos]]]]]:
- <interface>:为MMC。
- <dev[:part]>:对应/dev/mmcblkXpY,其中part是16进制格式。
- addr:内存地址。
- filename [bytes [pos]]:文件名,后面不加bytes和pos则表示完整文件。
2 Linux下MMC/SD/SDIO
Linux下MMC/SD/SDIO已经相当成熟,对于大部分主流SDHCI IP已经支持完整。
当对一个IP进行调试时,修改的大部分集中在DTS中:
- 配置IP基地址。
- 配置IP中断号。
- 配置IP所用到的时钟,包括AHB和工作时钟。
- 配置IP用到的Regulator。
- 配置IP pinctrl。
然后:
- 在调试中根据不同规格的卡,可能需要切换IO电压,Core工作电压。
- 如有需要可能要进行IO驱动能力调试。
然后就是可能使用到的ext4文件系统支持,以及一些工具mmc_test、mmc、fdisk、mkfs.ext4、gdisk等使用进行兼容性和性能测试。
2.1 MMC/SD/SDIO配置以及主要文件
MMC/SD/SDIO涉及到的内容包括:
- 用户空间的工具和sysfs节点。
- 内核的MMC Core和MMC/SD/SDIO驱动,以及相关的vfs、ext4等文件系统、block层框架等等。
- 硬件包括集成到SoC的SDHCI以及外设。
Linux下对MMC/SD/SDIO的配置如下:
Device Drivers ->MMC/SD/SDIO card support ->MMC block device driver ->Number of minors per block device
->MMC host test driver--支持mmc_test模块,可以对MMC/SD卡进行一系列读写测试。
->MMC host drivers debugging--打开CONFIG_MMC_DEBUG。
->Secure Digital Host Controller Interface support ->SDHCI platform and OF driver helper--基于Platform Device和OF驱动实现的SDHCI Host驱动。 ->Command Queue Host Controller Interface support--Arasan SDHCI需要支持Command Queue。
File systems
->The Extended 4 (ext4) filesystem--支持ext4文件系统。
MMC/SD文件位于/drvers/mmc下:
drivers/mmc/ ├── core │ ├── block.c--MMC块设备相关API。 │ ├── bus.c--MMC总线注册注销,以及总线上Device/Driver注册注销接口。 │ ├── core.c-- │ ├── debugfs.c--在/sys/kernel/debug/mmcX/下创建Host以及Device的调试信息。 │ ├── host.c--MMC Host一类设备的分配等API。 │ ├── mmc.c--提供mmc_attach_mmc()函数,MMC Card初始化。 │ ├── mmc_ops.c--MMC/SD协议层操作函数。 │ ├── mmc_test.c--创建mmc_test模块。 │ ├── pwrseq.c--MMC Power Sequence管理,提供注册注销接口,以及PrePowerOn/PostPowerOn/PowerOff/Reset等接口调用。 │ ├── pwrseq_emmc.c--emmc PowerSequence。 │ ├── pwrseq_simple.c--simple PowerSequence。 │ ├── queue.c--初始化struct mmc_queue,主要处理Block设备的请求队列。 │ ├── regulator.c--MMC/SD获取Regulator,以及使用Regulator接口函数。 │ ├── sd.c--提供mmc_attach_sd()函数,SD Card初始化。
│ ├── sd_ops.c-- │ ├── sdio_bus.c--SDIO总线注册,以及Device/Driver注册接口。 │ ├── sdio.c--提供mmc_attach_sdio(),SDIO Card初始化。
│ ├── sdio_ops.c--SDIO相关操作函数。 │ ├── sdio_cis.c--Common/Function CIS信息读取和释放。 │ ├── sdio_io.c--SDIO IO层面的操作API。 │ ├── sdio_irq.c--
│ ├── slot-gpio.c--SD Card热插拔相关GPIO操作API。
├── host │ ├── cqhci.c--通用的SDHCI支持Command Queue功能API接口。 │ ├── sdhci.c--对SDHCI提供一系列API接口。 │ ├── sdhci-of-arasan.c--Arasan基于DeviceTree的SDHCI驱动模块。 │ ├── sdhci-pltfm.c--SDHCI作为platform device的通用API接口。
2.2 Linux MMC/SD/SDIO框架
MMC框架大体如下:
Kernel的MMC框架可以分为3部分:
- MMC Core:负责Host Controller、MMC/SD/SDIO Card等硬件抽象,提供Platform Device、OF、Regulator、PowerSeq、Clock、debugfs等功能。提供统一的Host Controller驱动API,以及MMC、SD卡驱动。
- Host Driver:MMC/SD/SDIO Host设备驱动。
- 外设驱动:包括SDIO外设等,其中SD卡、MMC等驱动在MMC Core中已经提供。
2.3 SD DTS示例
程序的IP驱动开发主要工作量集中在DTS的配置:
sd0-sdhci@xxxxx { compatible = "arasan,sdhci-4.9a"; reg = <0x00000000 0x1000>;--IP基地址以及地址范围配置。 clock-names = "clk_xin", "clk_ahb";--工作时钟和AHB寄存器时钟。 clocks = <&clk_xin>, <&clk_ahb>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI xx IRQ_TYPE_LEVEL_HIGH>;--中断。
vmmc-supply = <&vddsdcore>;--给Core供电Regulator。
vqmmc-supply = <&vddsdio>;--给IO供电的Regulator。
bus-width = <4>;--SD总线位宽。
cap-sd-highspeed;--支持SD高速时序。
sd-uhs-sdr12;
...
sd-uhs-ddr50;--支持的UHS类型。
non-removable;--不支持热插拔。不配置则支持热插拔。
no-sdio;--初始化时禁止使用SDIO命令。
no-mmc;--初始化时禁止使用MMC命令。
pinctrl-name = "XXX";
pinctrl-0 = <&sdio_sd_pin>;
};
关于DTS的解释在Documentation/devicetree/bindings/mmc/mmc-controller.yaml中有详细解释《mmc-controller.yaml - Documentation/devicetree/bindings/mmc/mmc-controller.yaml - Linux source code (v6.5.8) - Bootlin》。
2.4 Linux MMC/SD/SDIO代码解读
MMC/SD相关模块:
mmc_init:注册mmc、sdio总线,以及mmc_host class。
mmc_pwrseq_simple_driver_init:Simple PowerSequence模块初始化,包含pre_power_on/post_power_on/power_off接口。
mmc_pwrseq_emmc_driver_init:MMC的PowerSequence模块初始化,仅包含reset。
mmc_blk_init:注册rpmb_bus,rpmb字符设备;祖册MMC块设备号,注册mmc_driver驱动。主要是为后续块设备使用做准备。
mmc_test_init:mmc_test模块,提供对MMC设备的一系列测试功能。
sdhci_drv_init:空白模块,提供SDHCI API。
sdhci_pltfm_drv_init:空白模块,提供Platform Device的API。
sdhci_arasan_driver_init:Arasan SDHCI控制器的驱动。
其中mmc_bus的总线、设备、驱动关系如下:
MMC相关总线以及Class初始化:
mmc_init ->mmc_register_bus--注册mmc总线,创建/sys/bus/mmc。 ->mmc_register_host_class--创建mmc_host class,位于/sys/class/mmc_host。 ->sdio_register_bus--注册sdio总线,创建/sys/bus/sdio。
mmc_blk_init ->bus_register ->alloc_chrdev_region--注册rpmb字符设备。 ->register_blkdev--注册MMC块设备。 ->mmc_register_driver
Arasan SDHCI初始化流程:
sdhci_arasan_probe
->sdhci_pltfm_init
->devm_platform_ioremap_resource--映射寄存器。
->platform_get_irq--获取中断号。
->sdhci_alloc_host--分配struct sdhci_host结构体,并注册以及进行设备扫描等。
->devm_clk_get--获取clk_ahb和clk_xin两个时钟。clk_ahb是寄存器工作时钟,clk_xin是SD工作时钟。
->sdhci_arasan_add_host
->sdhci_setup_host--主要填充struct sdhci_host结构体。
->mmc_regulator_get_supply--获取vmmc和vqmmc两个Regulator。vmmc为SD Core供电,vqmmc为IO供电。
->cqhci_init--CommandQueue功能初始化。
->__sdhci_add_host--为SDHCI Host创建工作队列、timer、中断处理等,然后初始化Host。
->sdhci_init
->mmc_add_host
->sdhci_enable_card_detection
MMC/SD/SDIO卡上电流程:
sdhci_pltfm_init->sdhci_alloc_host->mmc_alloc_host(创建delayed_work,1HZ扫描一次)
->mmc_rescan->mmc_rescan_try_req
->mmc_power_up--首先上电但不提供时钟。 ->mmc_attach_sd--按照SDIO->SD->MMC的顺序依次尝试挂载。
->mmc_attach_bus--配置mmc总线操作函数集为mmc_sd_ops。 ->mmc_sd_init_card--检测并初始化SD卡。 ->mmc_sd_get_cid--从SD卡读取CID。 ->mmc_set_uhs_voltage->mmc_host_set_uhs_voltage->mmc_set_signal_voltage(host->ops->start_signal_voltage_switch)--如果是UHS则设置IO电压。 ->sdhci_start_signal_voltage_switch ->mmc_regulator_set_vqmmc--调用DTS配置的Regulator配置IO电压。
->mmc_alloc_card
->mmc_sd_setup_card--读取SD卡的SCR/SSR/Read-Only等属性。
->mmc_sd_init_uhs_card--UHS-I类型SD卡初始化。
->mmc_execute_tuning
->sdhci_execute_tuning(host->ops->execute_tuning)
->sdhci_start_tuning
->__sdhci_execute_tuning
->sdhci_send_tuning
->sdhci_end_tuning
sdhci_add_host/__sdhci_add_host创建一个SDHCI Host,并进行初始化。
__sdhci_add_host
->mmc_add_host
->mmc_add_host_debugfs
->mmc_start_host ->mmc_power_up->mmc_set_ios->sdhci_set_ios ->sdhci_arasan_set_power--Arasan驱动的上下电接口。 ->mmc_regulator_set_ocr--根据OCR的VDD配置,调用Regulator API进行电压设置。
->sdhci_set_power_noreg ->__mmc_detect_change
2.5 Linux下MMC工具
2.5.1 mmc_test:MMC/SD设备内核测试
使用mmc_test驱动可以对MMC设备进行一系列读写测试。
默认情况下MMC/SD设备和mmcblk驱动绑定:
ls /sys/bus/mmc/drivers/* /sys/bus/mmc/drivers/mmc_test: bind uevent unbind /sys/bus/mmc/drivers/mmcblk: bind mmc0:0001 uevent unbind
如果要使用mmc_test对MMC/SD进行测试,首先需要将设备和mmcblk驱动解绑,然后和mmc_test驱动绑定。再进行测试。
步骤如下:
1. 将设备和mmcblk驱动解绑。
echo mmc0:0001 > /sys/bus/mmc/drivers/mmcblk/unbind
2. 将设备和mmc_test绑定。
# echo mmc0:0001 > /sys/bus/mmc/drivers/mmc_test/bind # [ 4082.341913] 000: mmc_test_register_dbgfs_file [ 4082.342008] 000: mmc_test mmc0:0001: Card claimed for testing.
3. 查看设备的测试用例列表。
cat /sys/kernel/debug/mmc0/mmc0:0001/testlist 0: Run all tests 1: Basic write (no data verification) ...50: Commands during non-blocking read - use Set Block Count (CMD23) 51: Commands during non-blocking write - use Set Block Count (CMD23)
4. 执行测试用例。
echo 5 > /sys/kernel/debug/mmc0/mmc0:0001/test
2.5.2 mmc:MMC/SD设备用户空间配置工具
mmc对MMC/SD设备进行配置,包括WriteProtect、rpmb分区、boot分区、csd/cid/scr等等操作。
mmc extcsd read <device>--mmc extcsd read /dev/mmcblk0。 Print extcsd data from <device>.... mmc cache enable <device>
mmc cache disable <device>
mmc csd read <device path>
mmc cid read <device path>--获取/sys/class/mmc_host/mmcX/mmcX:YYYY/对应设备的CID信息。格式为mmc cid read /sys/class/mmc_host/mmcX/mmcX:YYYY。
mmc scr read <device path>
mmc ffu <image name> <device> Run Field Firmware Update with <image name> on <device>.
2.6 GPT格式说明、Linux对GPT处理、GPT工具
2.6.1 MBR格式说明
MBR位于LBA0,包含MBR Header和MBR partition Entry。
MBR Header结构体如下:
Mnemonic |
Byte Offset |
Byte Length |
Description |
BootCode |
0 |
424 |
x86 code used on a non-UEFI system to select an MBR partition record and load the first logical block of that partition . This code shall not be executed on UEFI systems. |
UniqueMB RDiskSignature |
440 |
4 |
Unique Disk Signature This may be used by the OS to identify the disk from other disks in the system. This value is always written by the OS and is never written by EFI firmware. |
Unknown |
444 |
2 |
Unknown. This field shall not be used by UEFI firmware. |
PartitionRecord |
446 |
16*4 |
每个MBR partition Entry大小为16字节,最多存放4个MBR分区。MBR partition Entry参考Legacy MBR Partition Record。 |
Signature |
510 |
2 |
固定设置为0xAA55,其中510字节为0x55,511字结为0xAA。 |
Reserved |
512 |
Logical BlockSize - 512 |
The rest of the logical block, if any, is reserved. |
其中每个MBR partition Entry结构体如下:
Mnemonic |
Byte Offset |
Byte Length |
Description |
BootIndicator |
0 |
1 |
0x80 indicates that this is the bootable legacy partition. Other values indicate that this is not a bootable legacy partition. This field shall not be used by UEFI firmware. |
StartingCHS |
1 |
3 |
Start of partition in CHS address format. This field shall not be used by UEFI firmware. |
OSType |
4 |
1 |
Type of partition. See See OS Types . |
EndingCHS |
5 |
3 |
End of partition in CHS address format. This field shall not be used by UEFI firmware. |
StartingLBA |
8 |
4 |
Starting LBA of the partition on the disk. This field is used by UEFI firmware to determine the start of the partition. |
SizeInLBA |
12 |
4 |
Size of the partition in LBA units of logical blocks. This field is used by UEFI firmware to determine the size of the partition. |
2.6.2 protective MBR
当MBR OSType为0xee时,表示LBA0存放的是protective MBR数据。
protective MBR Header如下,红色为相对于MBR的差异部分:
Mnemonic |
Byte Offset |
Byte Length |
Contents |
Boot Code |
0 |
440 |
Unused by UEFI systems. |
Unique MBR Disk Signature |
440 |
4 |
Unused. Set to zero. |
Unknown |
444 |
2 |
Unused. Set to zero. |
Partition Record |
446 |
16*4 |
Array of four MBR partition records. Contains:
• one partition record as defined See Table (below); and
• three partition records each set to zero.
|
Signature |
510 |
2 |
Set to 0xAA55 (i.e., byte 510 contains 0x55 and byte 511 contains 0xAA). |
Reserved |
512 |
Logical Block Size - 512 |
The rest of the logical block, if any, is reserved. Set to zero. |
protective MBR partition Entry如下:
Mnemonic |
Byte Offset |
Byte Length |
Description |
BootIndicator |
0 |
1 |
Set to 0x00 to indicate a non-bootable partition. If set to any value other than 0x00 the behavior of this flag on non-UEFI systems is undefined. Must be ignored by UEFI i mplementations. |
StartingCHS |
1 |
3 |
Set to 0x000200, corresponding to the Starting LBA field. |
OSType |
4 |
1 |
Set to 0xEE (i.e., GPT Protective) |
EndingCHS |
5 |
3 |
Set to the CHS address of the last logical block on the disk. Set to 0xFFFFFF if it is not possible to represent the value in this field. |
StartingLBA |
8 |
4 |
Set to 0x00000001 (i.e., the LBA of the GPT Partition Header). |
SizeInLBA |
12 |
4 |
Set to the size of the disk minus one. Set to 0xFFFFFFFF if the size of the disk is too large to be represented in this field. |
下图为容量小于等于LBA 0xFFFFFFFF的情况:
下图为容量大于LBA 0xFFFFFFFF的情况:
2.6.1 GPT格式说明
《5. GUID Partition Table (GPT) Disk Layout — UEFI Specification 2.10 documentation》中定义了GPT格式。
LBA(Logical Block Address)0是第一个Logical Block,用于保存MBR或者PMBR。
GPT由GPT Header和GPT Partition Entries组成:
- GPT Header包括签名、版本号,已经用于校验的CRC32等信息。
- GPT Partition Entry包含了GUID,以及LBA范围、属性、分区名等信息。
GPT Header如下:
Mnemonic |
Byte Offset |
Byte Length |
Description |
Signature |
0 |
8 |
Identifies EFI-compatible partition table header. This value must contain the ASCII string “EFI PART”, encoded as the 64-bit constant 0x54 52415020494645. |
Revision |
8 |
4 |
The revision number for this header. This revision value is not related to the UEFI Specification version. This header is version 1.0, so the correct value is 0x00010000. |
HeaderSize |
12 |
4 |
Size in bytes of the GPT Header. The HeaderSize must be greater than or equal to 92 and must be less than or equal to the logical block size. |
HeaderCRC32 |
16 |
4 |
CRC32 checksum for the GPT Header structure. This value is computed by setting this field to 0, and computing the 32-bit CRC for HeaderSize bytes. |
Reserved |
20 |
4 |
Must be zero. |
MyLBA |
24 |
8 |
The LBA that contains this data structure. |
AlternateLBA |
32 |
8 |
LBA address of the alternate GPT Header. |
FirstUsableLBA |
40 |
8 |
The first usable logical block that may be used by a partition described by a GUID Partition Entry. |
LastUsableLBA |
48 |
8 |
The last usable logical block that may be used by a partition described by a GUID Partition Entry. |
DiskGUID |
56 |
16 |
GUID that can be used to uniquely identify the disk. |
Par titionEntryLBA |
72 |
8 |
The starting LBA of the GUID Partition Entry array. |
NumberOfPa rtitionEntries |
80 |
4 |
The number of Partition Entries in the GUID Partition Entry array. |
SizeOf PartitionEntry |
84 |
4 |
The size, in bytes, of each the GUID Partition Entry structures in the GUID Partition Entry array. This field shall be set to a value of 128 x 2 n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.). NOTE: Previous versions of this specification allowed any multiple of 8.. |
PartitionE ntryArrayCRC32 |
88 |
4 |
The CRC32 of the GUID Partition Entry array. Starts at Par titionEntryLBA and is computed over a byte length of NumberOfP artitionEntries * SizeOfP artitionEntry. |
Reserved |
92 |
BlockSize - 92 |
The rest of the block is reserved by UEFI and must be zero. |
GPT Partition Entry如下:
Mnemonic |
Byte Offset |
Byte Length |
Description |
PartitionTypeGUID |
0 |
16 |
Unique ID that defines the purpose and type of this Partition. A value of zero defines that this partition entry is not being used. |
UniquePartitionGUID |
16 |
16 |
GUID that is unique for every partition entry. Every partition ever created will have a unique GUID. This GUID must be assigned when the GPT Partition Entry is created. The GPT Partition Entry is created whenever the NumberOfPa rtitionEntries in the GPT Header is increased to include a larger range of addresses. |
StartingLBA |
32 |
8 |
Starting LBA of the partition defined by this entry. |
EndingLBA |
40 |
8 |
Ending LBA of the partition defined by this entry. |
Attributes |
48 |
8 |
Attribute bits, all bits reserved by UEFI ( Defined GPT Partition Entry — Partition Type GUIDs. |
PartitionName |
56 |
72 |
Null-terminated string containing a human-readable name of the partition. |
Reserved |
128 |
SizeOf PartitionEntry - 128 |
The rest of the GPT Partition Entry, if any, is reserved by UEFI and must be zero. |
GPT一般存在两份:在开始的主分区表和结尾的备份分区表。
GPT Header必须位于LBA1,备份GPT Header必须位于最后一个LBA。
GPT Header定义了First Usable LBA和Last Usable LBA,所有的数据必须在者之间存储。
Primary GPT Partition Entry数组必须在Primary GPT Header之后,First Usable LBA之前。
Backup GPT Partition Entry必须在Backup GPT Header之前,Last Usable LBA之后。
GPT Partition Entry数组可以包括0或者多个GPT PartitionEntry,每个partition必须不交叉覆盖。
必须为GPT Partition Entry数组预留16384字节分区。
如果块大小为512B,则需要预留34及以上块:1(PMBR)+1(GPT Header)+16KB(GPT Partition Entry)。
如果块大小为4096B,则需要预留6及以上块:1(PMBR)+1(GPT Header)+16KB(GPT Partition Entry)。
另一张图表达更清晰:
GPT Header独占一个LBA;4个GPT Entry占用一个LBA(512);最多128个GPT Entry,共占用32个LBA;所以需要给GPT预留34个LBA。
Backup GPT Header在最后一个LBA;Backup GPT共占用33个LBA。
图片来自于《Linux存储管理》。
2.6.2 Linux GPT处理
Linux GPT Header和GPT Entry如下:
typedef struct _gpt_header { __le64 signature; __le32 revision; __le32 header_size; __le32 header_crc32; __le32 reserved1; __le64 my_lba; __le64 alternate_lba; __le64 first_usable_lba; __le64 last_usable_lba; efi_guid_t disk_guid; __le64 partition_entry_lba; __le32 num_partition_entries; __le32 sizeof_partition_entry; __le32 partition_entry_array_crc32; /* The rest of the logical block is reserved by UEFI and must be zero. * EFI standard handles this by: * * uint8_t reserved2[ BlockSize - 92 ]; */ } __packed gpt_header; typedef struct _gpt_entry_attributes { u64 required_to_function:1; u64 reserved:47; u64 type_guid_specific:16; } __packed gpt_entry_attributes; typedef struct _gpt_entry { efi_guid_t partition_type_guid; efi_guid_t unique_partition_guid; __le64 starting_lba; __le64 ending_lba; gpt_entry_attributes attributes; efi_char16_t partition_name[72 / sizeof (efi_char16_t)]; } __packed gpt_entry;
在mmcblk驱动中对GPT分区进行了处理:
mmc_blk_probe ->mmc_add_disk ->device_add_disk->__device_add_dsk ->register_disk ->blkdev_get ->__blk_dev_get ->bdev_disk_changed ->blkdev_reread_part ->rescan_partitions
->check_partition--遍历check_part进行分区表解析,其中efi_partition要在msdos_partition之前。由于两者可以兼容,所以优先使用GPT。 ->efi_partition--从设备中读取GPT Header/Entry信息,并填充struct parsed_partitions结构体。
->find_valid_gpt--校验GPT是否有效,如有效则将数据读取到变量中。
->is_pmbr_valid--判断LBA0是否是PMBR。
->is_gpt_valid--读取GPT Header,并通查是否有效;读取GPT Entry方便后续处理。并比较Primary GPT和Backup GPT。
->is_pte_valid--检查PTE的GUID、LBA范围确认是否有效。
->put_partition--将从GPT Entry解析的分区信息写到struct parsed_partitions中。
->add_partition--向通用磁盘分区管理添加一个分区信息。
实例GPT Header和GPT Entry分析:
0000000 0000 0000 0000 0000 0000 0000 0000 0000 *0000400 3339 8da6 0007 60c0 36c4 3a08 23c8 0809--LBA2即为第一个GPT Entry的起始地址,每个GPT Entry占用0x80字节。partition_type_guid(16)分区类型的GUID。
00001b0 0000 0000 0000 0000 0000 0000 0000 0000--第一个分区的16字节。BootIndicator为0x00;StartingCHS在PMBR时固定为0x000200,表示第一个LBA起始位置。 00001c0 0002 8aee 8208 0001 0000 ffff 001f 0000--OSType为0xee,表示被protective MBR使用。EndingCHS为0x08828a。StartingLBA固定为0x00000001。SizeInLBA为0x001fffff,总大小减1。 00001d0 0000 0000 0000 0000 0000 0000 0000 0000 * 00001f0 0000 0000 0000 0000 0000 0000 0000 aa55--第510字节固定为0x55,第511字节固定为0xAA。 0000200 4645 2049 4150 5452 0000 0001 005c 0000--GPT Header位于LBA1,对于512 Sector即为0x200地址,并且独占一个Sector。分别是:signature(8);revision(4);header_size(4)为92字节。 0000210 4a0b 71b9 0000 0000 0001 0000 0000 0000--header_crc32(4);reserved1(4);my_lba(8)为1说明Header位于LBA1。 0000220 ffff 001f 0000 0000 0022 0000 0000 0000--alternate_lba(8)指示Backup GPT Header所在LBA;first_usable_lba(8)。 0000230 ffde 001f 0000 0000 38a8 368e 8cd1 4720--last_usable_lba(8)+disk_guid(16)。 0000240 0ab1 e8d2 8d26 4737 0002 0000 0000 0000--partition_entry_lba(8),存放GPT Entry的其起始LBA。 0000250 0080 0000 0080 0000 062f 52b8 0000 0000--num_partition_entries(8)是GPT Entry数量,共128个。sizeof_partition_entry(8)指示GPT Entry结构体大小,共128字节。partition_entry_array_crc32(8)是GPT Entry数组的crc32校验和。。 0000260 0000 0000 0000 0000 0000 0000 0000 0000 *
0000410 9613 9545 aa7a 47a2 2dbf 6b14 6f50 7f3e--unique_partition_guid(16)分区的GUID。
0000420 0044 0000 0000 0000 0143 0000 0000 0000--starting_lba(8)分区起始LBA;ending_lba(8)分区结束LBA。
0000430 0000 0000 0000 0000 0042 004c 0032 0000--attributes(8);partition_name(72)一个字符占2字节,最多36字符。
0000440 0000 0000 0000 0000 0000 0000 0000 0000
*
0000480 3339 8da6 0007 60c0 36c4 3a08 23c8 0809
0000490 9f76 f2a4 0953 44d8 6081 a83a 43a0 9d5b
00004a0 0144 0000 0000 0000 0243 0000 0000 0000
00004b0 0000 0000 0000 0000 0042 004c 0032 0000
00004c0 0000 0000 0000 0000 0000 0000 0000 0000
...头部和尾部的GPT Entry内容一致。
*
0000c00 3daf 0fc6 8483 4772 798e 693d 47d8 e47d
0000c10 50bc 9868 1c30 457a eca3 d5a3 36f3 0ed7
0000c20 4644 0001 0000 0000 c643 0001 0000 0000
0000c30 0000 0000 0000 0000 0072 006f 006f 0074
0000c40 0066 0073 0000 0000 0000 0000 0000 0000
0000c50 0000 0000 0000 0000 0000 0000 0000 0000
*
3fffbe00 3339 8da6 0007 60c0 36c4 3a08 23c8 0809
3fffbe10 9613 9545 aa7a 47a2 2dbf 6b14 6f50 7f3e
3fffbe20 0044 0000 0000 0000 0143 0000 0000 0000
3fffbe30 0000 0000 0000 0000 0042 004c 0032 0000
3fffbe40 0000 0000 0000 0000 0000 0000 0000 0000
*
3ffffe00 4645 2049 4150 5452 0000 0001 005c 0000--Backup GPT Header在最后一个LBA,独占一个LBA。和Primary GPT Header主要差别在my_lba/alternate_lba/partition_entry_lba。
3ffffe10 3bd7 e943 0000 0000 ffff 001f 0000 0000--由于GPT Header内容不同,header_crc32有差异。my_lba只是当前GPT Header所在LBA号。
3ffffe20 0001 0000 0000 0000 0022 0000 0000 0000--alternate_lba(8)指示Primary GPT Header所在LBA。
3ffffe30 ffde 001f 0000 0000 38a8 368e 8cd1 4720
3ffffe40 0ab1 e8d2 8d26 4737 ffdf 001f 0000 0000--指示Backup GPT Entry所在LBA,0x00f1ffdf*512=0x3fffbe00。
3ffffe50 0080 0000 0080 0000 062f 52b8 0000 0000
3ffffe60 0000 0000 0000 0000 0000 0000 0000 0000
*
40000000
2.6.3 分区处理工具
fdisk/cfdisk/sfdisk
fdisk是一个创建和管理分区表的工具,支持GPT、MBR、Sun、SGI和BSD分区表。
通过sudo fdisk -u /dev/sdX对设备进行分区管理:
GPT M enter protective/hybrid MBR Generic d delete a partition F list free unpartitioned space l list known partition types n add a new partition p print the partition table t change a partition type v verify the partition table i print information about a partition Misc m print this menu x extra functionality (experts only) Script I load disk layout from sfdisk script file O dump disk layout to sfdisk script file Save & Exit w write table to disk and exit q quit without saving changes Create a new label g create a new empty GPT partition table G create a new empty SGI (IRIX) partition table o create a new empty DOS partition table s create a new empty Sun partition table
sfdisk是基于fdisk,处理sfdisk脚本进行分区管理。适合于批量处理。
cfdisk是基于curses的fdisk包装工具,操作更友好。适合于手动调试。
gdisk/cgdisk/sgdisk
gdisk主要处理GPT分区,也支持MBR处理。sgdisk是支持gdisk脚本化处理的包装工具,适合批量处理;cfdisk是基于curses的gdisk包装工具,适合手动调整。
b back up GPT data to a file c change a partition's name d delete a partition i show detailed information on a partition l list known partition types n add a new partition o create a new empty GUID partition table (GPT) p print the partition table q quit without saving changes r recovery and transformation options (experts only) s sort partitions t change a partition's type code v verify disk w write table to disk and exit x extra functionality (experts only) ? print this menu
创建GPT分区时分区类型如下:
0700 Microsoft basic data 0c01 Microsoft reserved 2700 Windows RE 3000 ONIE boot 3001 ONIE config 3900 Plan 9 4100 PowerPC PReP boot 4200 Windows LDM data 4201 Windows LDM metadata 4202 Windows Storage Spaces 7501 IBM GPFS 7f00 ChromeOS kernel 7f01 ChromeOS root 7f02 ChromeOS reserved 8200 Linux swap 8300 Linux filesystem 8301 Linux reserved 8302 Linux /home 8303 Linux x86 root (/) 8304 Linux x86-64 root (/) 8305 Linux ARM64 root (/) 8306 Linux /srv 8307 Linux ARM32 root (/) 8308 Linux dm-crypt 8309 Linux LUKS 830a Linux IA-64 root (/) 830b Linux x86 root verity 830c Linux x86-64 root verity 830d Linux ARM32 root verity 830e Linux ARM64 root verity 830f Linux IA-64 root verity 8310 Linux /var 8311 Linux /var/tmp 8400 Intel Rapid Start 8500 Container Linux /usr 8501 Container Linux resizable rootfs 8502 Container Linux /OEM customization 8503 Container Linux root on RAID 8e00 Linux LVM a000 Android bootloader a001 Android bootloader 2 a002 Android boot 1
parted
parted支持MBR和GPT分区创建和管理。
partx
提醒内核增加或者删除分区操作,本身并不改变分区设置。
fixparts
提供对MBR分区表修复功能。
2.7 MMC裸烧镜像制作
首先给GPT所在位置请0;然后
sgdisk -og -a 1 <device>--保留MBR和BSD分区信息,避免被破坏;并清空GPT分区信息。 sgdisk -a 1 -n 1:34:545 -c 1:fsbl1 -t 1:8301 <device>--创建GPT分区。 dd if=<Input> of=<Device> conv=fdatasync,notrunc seek=1 bs=69747712--更新文件到分区中。
参考文档:《Linux MMC framework(1)_软件架构 (wowotech.net)》《Linux MMC介绍》《emmc模块》。
制作ext4文件系统镜像参考:《一个版本烧录过程中记录:fdisk、mkfs.ext4、make_ext4fs、img2simg、simg2img》、《Buildroot创建ramdisk、ext4、ubifs镜像,以及mkfs.ext4/mkfs.ubifs/cpio的使用》。
2.8 MMC/SD相关sysfs节点
MMC/SD调试节点:
/sys/kernel/debug/mmc0/ |-- caps--在include/linux/mmc/host.h中定义。 |-- caps2--在include/linux/mmc/host.h中定义。 |-- clock--当前SDHCI工作时钟。 |-- ios `-- mmc0:59b4 |-- state `-- status
MMC/SD Host和Device作为Block Device的sysfs 节点:
mmc_blk_probe ->mmc_blk_alloc
->mmc_blk_alloc_req->mmc_init_queue->blk_mq_init_queue->blk_mq_init_alloated_queue->blk_mq_realloc_hw_ctxs->blk_mq_alloc_and_init_hctx->blk_mq_alloc_hctx
->blk_mq_hctx_kobj_init--创建mmcX:YYYY/block/mq节点。 ->mmc_add_disk->device_add_disk->__device_add_dsk ->register_disk ->blkdev_get ->__blk_dev_get ->bdev_disk_changed ->blkdev_reread_part ->rescan_partitions ->add_partition--创建的设备分区属性在part_type中,在mmcX:YYYY/block/mmcblkZ节点。 ->blk_register_queue ->sysfs_create_group(&q->kobj, &queue_attr_group)--创建mmcX:YYYY/block/queue节点。
一个SD作为Block设备的sysfs实例:
/sys/class/mmc_host/mmc0/--SDHCI层级。 |-- device -> ../../../4020000.sd0-sdhci |-- mmc0:59b4--MMC/SD卡层级。 | |-- block--MMC/SD卡作为block设备的sysfs。 | | `-- mmcblk0 | | |-- alignment_offset | | |-- bdi -> ../../../../../../../virtual/bdi/179:0 | | |-- capability | | |-- dev | | |-- device -> ../../../mmc0:59b4 | | |-- discard_alignment | | |-- events | | |-- events_async | | |-- events_poll_msecs | | |-- ext_range | | |-- force_ro | | |-- hidden | | |-- holders | | |-- inflight | | |-- mmcblk0p1--在block/partition-generic.c的part_attrs中创建,显示当前分区设备的信息。 | | | |-- alignment_offset | | | |-- dev | | | |-- discard_alignment | | | |-- holders | | | |-- inflight | | | |-- partition | | | |-- power | | | |-- ro | | | |-- size | | | |-- start | | | |-- stat | | | |-- subsystem -> ../../../../../../../../../class/block | | | `-- uevent | | |-- mq--在block/blk-mq-sysfs.c的default_hw_ctx_attrs中创建,显示Multi-Queue相关信息。 | | | `-- 0 | | | |-- cpu0 | | | |-- cpu_list | | | |-- nr_reserved_tags | | | `-- nr_tags | | |-- power | | |-- queue--在block/blk-sysfs.c的queue_attrs中创建,显示block设备queue属性。 | | | |-- add_random | | | |-- chunk_sectors | | | |-- dax | | | |-- discard_granularity | | | |-- discard_max_bytes | | | |-- discard_max_hw_bytes | | | |-- discard_zeroes_data | | | |-- fua | | | |-- hw_sector_size | | | |-- io_poll | | | |-- io_poll_delay | | | |-- io_timeout | | | |-- iosched | | | | |-- fifo_batch | | | | |-- front_merges | | | | |-- read_expire | | | | |-- write_expire | | | | `-- writes_starved | | | |-- iostats | | | |-- logical_block_size | | | |-- max_discard_segments | | | |-- max_hw_sectors_kb | | | |-- max_integrity_segments | | | |-- max_sectors_kb | | | |-- max_segment_size | | | |-- max_segments | | | |-- minimum_io_size | | | |-- nomerges | | | |-- nr_requests | | | |-- nr_zones | | | |-- optimal_io_size | | | |-- physical_block_size | | | |-- read_ahead_kb | | | |-- rotational | | | |-- rq_affinity | | | |-- scheduler | | | |-- wbt_lat_usec | | | |-- write_cache | | | |-- write_same_max_bytes | | | |-- write_zeroes_max_bytes | | | `-- zoned | | |-- range | | |-- removable | | |-- ro | | |-- size | | |-- slaves | | |-- stat--当前设备的IO统计信息。 | | |-- subsystem -> ../../../../../../../../class/block | | `-- uevent | |-- cid | |-- csd | |-- date | |-- driver -> ../../../../../../bus/mmc/drivers/mmcblk--此设备所使用的驱动是mmcblk。 | |-- dsr | |-- erase_size | |-- fwrev | |-- hwrev | |-- manfid | |-- name | |-- ocr | |-- oemid | |-- power | |-- preferred_erase_size | |-- rca | |-- scr | |-- serial | |-- ssr | |-- subsystem -> ../../../../../../bus/mmc | |-- type | `-- uevent |-- power |-- subsystem -> ../../../../../class/mmc_host `-- uevent
2.8 Linux block相关工具
lsblk
显示块设备的详细信息。比如lsblk -f显示文件系统信息:
sda ├─sda1 vfat 4B20-EFC1 511M 0% /boot/efi ├─sda2 └─sda5 ext4 6cbcadd9-bc01-497f-aa2b-d0770aa0c374 10G 85% / sdb ├─sdb1 ... └─sdb17
blockdev
blockdev通过调用块设备的ioctl命令获取或设置块设备信息,主要包括IO size、Sector size,Physical block size、读写属性等。
sudo blockdev --report /dev/sdb RO RA SSZ BSZ StartSec Size Device rw 256 512 4096 0 31719424000 /dev/sdb
2.9 Linux启动相关配置
dts
在bootargs中添加如下内容在启动时挂载对应分区作为rootfs:
root=/dev/mmcblk0p13 rootfstype=ext4 rw rootwait
fstab
/dev/mmcblk0p7 /mnt/xxx ext4 defaults 0 0
2.9 关于ext4/block/mmc等的trace events
Linux提供了ext4/block/mmc等trace events辅助对这些模块进行调试。
/sys/kernel/debug/tracing/events/block/ |-- block_bio_backmerge |-- block_bio_bounce ... |-- block_touch_buffer |-- block_unplug /sys/kernel/debug/tracing/events/ext4/ |-- ext4_alloc_da_blocks |-- ext4_allocate_blocks ... |-- ext4_writepages |-- ext4_writepages_result |-- ext4_zero_range /sys/kernel/debug/tracing/events/mmc/ |-- mmc_request_done `-- mmc_request_start