UEFI EDK2环境下SD Express卡启动支持:BBR方案实现

概述

本文介绍在UEFI EDK2环境下,如何实现SD Express卡作为系统启动盘的支持。主要内容包括:

  1. 嵌入式平台启动的特殊挑战:不支持传统PCIe热插拔
  2. BBR(Bios Boot Record)方案:软件控制的模式切换
  3. EDK2框架整合:SdMmcPciHcDxe驱动与BayhubHost扩展
  4. 完整启动流程:从固件到操作系统的无缝衔接

1. 背景:嵌入式平台的启动挑战

1.1 PC平台 vs 嵌入式平台

特性 PC平台 嵌入式平台
存储介质 NVMe SSD、SATA SSD SD卡、eMMC、SD Express
启动方式 UEFI标准启动 定制BSP
热插拔支持 标准ACPI热插拔 不支持
SD卡槽 标准PCIe插槽 贴片式卡槽

1.2 SD Express的双重身份

SD Express卡具有两种工作模式:

1
2
3
4
5
6
7
8
9
10
11
12
┌─────────────────────────────────────────────────────────────────┐
│ SD Express Card │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [SD Mode] [PCIe/NVMe Mode] │
│ ┌──────────┐ ┌──────────┐ │
│ │ SD 3.0 │ │ PCIe 4.0 │ │
│ │ UHS-I │ ──────────► │ NVMe 1.4 │ │
│ │ ~100MB/s│ │ ~4GB/s │ │
│ └──────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

嵌入式平台需要在固件阶段就完成模式切换,让操作系统看到标准的NVMe设备。


2. BBR(Bios Boot Record)方案

2.1 方案原理

BBR方案是一种软件模拟热插拔的技术:

  1. 在UEFI固件阶段,SD Host Controller以SD模式枚举
  2. 检测到SD Express卡后,执行协议切换
  3. 切换到PCIe模式,重新枚举NVMe设备
  4. 操作系统启动时,看到标准NVMe启动设备

2.2 架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
┌─────────────────────────────────────────────────────────────────────────┐
│ UEFI EDK2 启动架构 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ SEC → PEI → DXE → BDS │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ SdMmcPciHcDxe Driver │ │
│ │ ┌───────────────────┐ ┌───────────────────┐ │ │
│ │ │ SDHCI Core │ │ BayhubHost.c │ │ │
│ │ │ (标准框架) │ │ (GG8特定实现) │ │ │
│ │ └───────────────────┘ └───────────────────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ SdMmcPassThru Protocol │ │ │
│ │ │ (SD/MMC命令传递) │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ SD Express 切换流程 │ │
│ │ 1. SD协议初始化 │ │
│ │ 2. 检测SD Express能力 (SCR[60]) │ │
│ │ 3. 执行SD 7.0切换序列 │ │
│ │ 4. PCIe模式重新枚举 │ │
│ │ 5. NVMe驱动接管 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ BlockIo → Boot Option │ │
│ │ 操作系统启动设备 → /dev/nvmeXn1 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘

3. EDK2框架整合

3.1 SdMmcPciHcDxe驱动架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────────────────────────────────┐
│ SdMmcPciHcDxe Driver │
├─────────────────────────────────────────────────────────────────┤
│ │
│ InitializeSdMmcPciHcDxe() │
│ │ │
│ ├── DriverBindingSupported() ← 匹配PCI Device ID │
│ │ │
│ └── DriverBindingStart() │
│ │ │
│ ├── LocatePciIo() ← 获取PCI I/O Protocol │
│ │ │
│ ├── BhtHostPciSupportType() ← Bayhub设备检测 │
│ │ │ │
│ │ └── 匹配: PCI_DEV_ID_GG8_1 ~ GG8_4 │
│ │ │
│ ├── BhtHostInit() ← GG8初始化 │
│ │ │
│ ├── SdMmcHcInit() ← SD Host Controller初始化 │
│ │ │
│ └── InstallSdMmcPassThru() ← 发布PassThru协议 │
│ │
└─────────────────────────────────────────────────────────────────┘

3.2 BayhubHost扩展实现

3.2.1 GG8设备识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// BayhubHost.c

// GG8 PCI Device ID定义
#define PCI_DEV_ID_GG8_1 0x9860
#define PCI_DEV_ID_GG8_2 0x9861
#define PCI_DEV_ID_GG8_3 0x9862
#define PCI_DEV_ID_GG8_4 0x9863

/**
检测Bayhub设备类型

@param PciIo PCI I/O Protocol指针

@retval eMMC_HOST eMMC控制器
@retval SD_HOST SD控制器(包括GG8系列)
@retval UNSUPPORT 不支持的设备
**/
UINT8 BhtHostPciSupportType(EFI_PCI_IO_PROTOCOL *PciIo)
{
PCI_TYPE00 Pci;
UINT8 HostType;

// 读取PCI配置空间
PciIo->Pci.Read(PciIo, EfiPciIoWidthUint32,
0, sizeof(Pci) / sizeof(UINT32), &Pci);

// 检查Vendor ID (Bayhub: 0x1217)
if (Pci.Hdr.VendorId != 0x1217) {
HostType = UNSUPPORT;
goto end;
}

// 保存Device ID供后续使用
BhtDeviceId = Pci.Hdr.DeviceId;

// 根据Device ID判断主机类型
switch (Pci.Hdr.DeviceId) {
case PCI_DEV_ID_SB0:
HostType = eMMC_HOST; // BH720 eMMC控制器
break;

// GG8系列: 0x9860 ~ 0x9863 (SD Express支持)
case PCI_DEV_ID_GG8_1:
case PCI_DEV_ID_GG8_2:
case PCI_DEV_ID_GG8_3:
case PCI_DEV_ID_GG8_4:
default:
HostType = SD_HOST; // SD控制器
break;
}

end:
return HostType;
}

3.2.2 GG8寄存器访问

GG8系列与其他Bayhub芯片的寄存器访问方式不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// GG8系列直接通过Memory Space访问
// 其他系列(如SDS0/SDS1)需要通过PCI Mapping访问

UINT32 PciBhtRead32(EFI_PCI_IO_PROTOCOL *PciIo, UINT32 offset)
{
UINT32 result;

if ((BhtDeviceId == PCI_DEV_ID_GG8_1) ||
(BhtDeviceId == PCI_DEV_ID_GG8_2) ||
(BhtDeviceId == PCI_DEV_ID_GG8_3) ||
(BhtDeviceId == PCI_DEV_ID_GG8_4))
{
// GG8: 直接MMIO访问
result = bht_readl(PciIo, offset);
} else {
// 其他: 通过PCI Mapping访问
// ... 复杂的映射逻辑 ...
}

return result;
}

3.2.3 GG8主机初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
EFI_STATUS EFIAPI BhtHostInit(
IN EFI_PCI_IO_PROTOCOL *PciIo,
IN UINT8 Slot
)
{
UINT32 value32;
EFI_STATUS Status;

// ===== 1. 设置200MHz基础时钟 =====
value32 = PciBhtRead32(PciIo, 0x304);
value32 &= 0x0000FFFF;
value32 |= 0x25100000; // 200MHz PLL设置
PciBhtWrite32(PciIo, 0x304, value32);

// ===== 2. 启用内部时钟 =====
value32 = BIT0;
Status = SdMmcHcOrMmio(PciIo, Slot, SD_MMC_HC_CLOCK_CTRL,
sizeof(value32), &value32);

// ===== 3. 复位PLL =====
// ... 省略部分代码 ...

// ===== 4. GG8特定: M/N/OD分频设置 =====
if ((BhtDeviceId == PCI_DEV_ID_GG8_1) ||
(BhtDeviceId == PCI_DEV_ID_GG8_2) ||
(BhtDeviceId == PCI_DEV_ID_GG8_3) ||
(BhtDeviceId == PCI_DEV_ID_GG8_4))
{
// 等待PLL解锁 (1CC[14] = 0)
Status = SdMmcHcRwMmio(PciIo, Slot, 0x1CC, TRUE,
sizeof(value32), &value32);
while (value32 & BIT14) {
gBS->Stall(100);
Status = SdMmcHcRwMmio(PciIo, Slot, 0x1CC, TRUE,
sizeof(value32), &value32);
}

// 解锁PCR访问
PciBhtAnd32(PciIo, 0xD0, ~(BIT31));

// 设置M/N/OD分频
value32 = PciBhtRead32(PciIo, 0x304);
value32 |= 0xffff0000;
value32 &= 0x2520ffff;
PciBhtWrite32(PciIo, 0x304, value32);

// 锁定PCR访问
PciBhtOr32(PciIo, 0xD0, (BIT31));
}

// ===== 5. 等待PLL锁定 =====
if ((BhtDeviceId == PCI_DEV_ID_GG8_1) ||
(BhtDeviceId == PCI_DEV_ID_GG8_2) ||
(BhtDeviceId == PCI_DEV_ID_GG8_3) ||
(BhtDeviceId == PCI_DEV_ID_GG8_4))
{
// 等待PLL锁定 (1CC[14] = 1)
Status = SdMmcHcRwMmio(PciIo, Slot, 0x1CC, TRUE,
sizeof(value32), &value32);
while (!(value32 & BIT14)) {
gBS->Stall(100);
Status = SdMmcHcRwMmio(PciIo, Slot, 0x1CC, TRUE,
sizeof(value32), &value32);
}
}

return Status;
}

4. INF文件配置

4.1 SdMmcPciHcDxe.inf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
## @file
# SdMmcPciHcDxe - SD/MMC PCI Host Controller Driver
#
# 支持SD Host Controller规范v3.0和64位系统地址支持

[Defines]
INF_VERSION = 0x01540012
BASE_NAME = SdMmcPciHcDxe
FILE_GUID = 8E325979-3FE1-4927-AAE2-8F5C4BD2AF0D
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.1
ENTRY_POINT = InitializeSdMmcPciHcDxe

[Sources]
SdMmcPciHcDxe.c # 主驱动
SdDevice.c # SD设备操作
SdMmcPciHci.c # Host Controller接口
BayhubHost.c # Bayhub特定实现 ← 关键文件
BayhubHost.h # Bayhub头文件

[Protocols]
gEfiPciIoProtocolGuid ## TO_START
gEfiSdMmcPassThruProtocolGuid ## BY_START

[LibraryClasses]
UefiBootServicesTableLib
UefiDriverEntryPoint
DebugLib
BaseMemoryLib

4.2 驱动加载顺序

在DSC文件中确保正确的加载顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Components]
# 1. 先加载PCI Bus Driver
MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf

# 2. 再加载SD/MMC Host Controller Driver
MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf

# 3. SD卡设备驱动
MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf

# 4. BlockIo支持
MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf

# 5. NVMe Driver (切换后由标准NVMe驱动接管)
MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf

5. SD Express切换流程

5.1 切换时序图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
┌─────────────────────────────────────────────────────────────────────────┐
│ SD Express 切换时序 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ UEFI DXE Phase │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Step 1: SD协议初始化 │ │
│ │ - 发送CMD0 (GO_IDLE_STATE) │ │
│ │ - 发送CMD8 (SEND_IF_COND) - 检测SD 2.0+ │ │
│ │ - 发送ACMD41 (SD_SEND_OP_COND) - 获取OCR │ │
│ │ - 发送CMD2 (ALL_SEND_CID) - 获取CID │ │
│ │ - 发送CMD3 (SEND_RELATIVE_ADDR) - 获取RCA │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Step 2: 检测SD Express能力 │ │
│ │ - 发送CMD8 (SEND_SCR) - 获取SCR寄存器 │ │
│ │ - 检查SCR[60] = 1 (SD Express Supported) │ │
│ │ - 检查SCR[51:60] (Bus Speed Control) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Step 3: 电压切换 (1.8V) │ │
│ │ - 发送CMD11 (VOLTAGE_SWITCH) │ │
│ │ - 等待卡进入1.8V信号模式 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Step 4: PCIe链路训练 │ │
│ │ - 等待卡发出PCIe clkreqn信号 │ │
│ │ - 执行PCIe Detect/Polling/LTraining │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Step 5: 模式切换寄存器写入 │ │
│ │ - 写PCR 0x54[8] = 1 (触发PCIe Switch) │ │
│ │ - 等待切换完成 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Step 6: PCI总线重新扫描 │ │
│ │ - PCIe Bus Driver发现新设备 │ │
│ │ - NVMe Driver枚举 │ │
│ │ - BlockIo安装 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ OS Boot Loader │
│ │ │
│ ▼ │
│ /dev/nvme0n1 (标准NVMe设备) │
│ │
└─────────────────────────────────────────────────────────────────────────┘

5.2 关键寄存器说明

寄存器 偏移 说明 GG8支持
PCR 0x304 0x304 PLL时钟设置 M/N/OD分频
PCR 0x1CC 0x1CC PLL控制 复位/锁定检测
PCR 0xD0 0xD0 PCR访问锁定 GG8需要解锁
PCR 0x54 0x54 PCIe Switch 模式切换触发

6. 完整启动流程

6.1 UEFI启动阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
UEFI Boot Flow:
─────────────────────────────────────────────────────────────

SEC Phase


PEI Phase
│ ├── 初始化最小CPU
│ ├── 初始化内存
│ └── 传递HOB给DXE


DXE Phase
│ ├── PciBusDxe ← 枚举PCI设备
│ ├── SdMmcPciHcDxe ← SD控制器驱动
│ │ ├── BayhubHost.c (GG8支持)
│ │ ├── BhtHostInit()
│ │ └── SdMmcPassThru Protocol
│ │
│ ├── SdDxe ← SD块设备驱动
│ │
│ └── NvmExpressDxe ← NVMe驱动 (切换后接管)


BDS Phase
│ ├── 检测启动设备
│ ├── 创建Boot Option
│ └── 加载Boot Loader


TSL/Run Phase
│ └── OS Loader启动


Exit Boot Services
└── 操作系统控制

6.2 与标准PC启动流程的对比

阶段 PC平台 嵌入式BBR方案
存储检测 BIOS POST阶段 UEFI DXE阶段
驱动加载 PCI枚举自动发现 定制SD驱动先加载
模式切换 N/A 软件模拟热插拔
NVMe发现 标准PCIe枚举 BBR后重新枚举
启动设备 标准NVMe设备 /dev/nvme0n1

7. 调试方法

7.1 UEFI Shell调试命令

1
2
3
4
5
6
7
8
9
10
11
12
# 查看PCI设备树
Shell> pci

# 查看SD控制器
Shell> pci 00:1c.0
Vendor 1217: Device 9860 (Bayhub GG8)

# 查看块设备
Shell> blk

# 手动触发SD Express切换
Shell> sddevicetest

7.2 Debug Log示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[SDHCI] BhtHostPciSupportType: Checking device 1217:9860
[SDHCI] Device is GG8 (0x9860), HostType = SD_HOST
[SDHCI] BhtHostInit: Setting 200MHz base clock
[SDHCI] BhtHostInit: Waiting for PLL lock...
[SDHCI] BhtHostInit: PLL locked
[SDHCI] SdMmcHcInit: Initializing SD Host Controller
[SDHCI] SdMmcHcInit: Card present detected
[SDHCI] SdCardInit: Sending CMD0 (GO_IDLE)
[SDHCI] SdCardInit: CMD8 response: 0x1AA
[SDHCI] SdCardInit: SD 2.0+ card detected
[SDHCI] SdCardInit: Sending ACMD41
[SDHCI] SdCardInit: OCR = 0x40FF8000 (SDXC, SD Express capable)
[SDHCI] SdCardInit: Reading SCR register
[SDHCI] SdCardInit: SCR[60:55] = 0x02 (SD Express supported)
[SDHCI] SdCardInit: Initiating SD Express switch...
[SDHCI] SdCardInit: Switching to 1.8V signaling
[SDHCI] SdCardInit: Waiting for PCIe clkreqn...
[SDHCI] SdCardInit: PCIe link training detected
[SDHCI] SdCardInit: Triggering PCIe switch (PCR 0x54[8] = 1)
[SDHCI] SdCardInit: Switch complete, waiting for rescan...
[PCIeBus] Device detected at 00:1c.1
[PCIeBus] NVMe device found: 1217:8760
[Nvme] Controller initialized
[BlockIo] BlockIo installed on handle

8. 总结

本文介绍了在UEFI EDK2环境下实现SD Express卡作为系统启动盘的完整方案:

8.1 核心技术点

  1. BayhubHost扩展:通过添加GG8设备支持,实现特定寄存器的正确访问
  2. BBR方案:软件模拟热插拔,解决嵌入式平台不支持硬件热插拔的问题
  3. 框架整合:将定制驱动无缝集成到标准EDK2框架中

8.2 与标准EDK2组件的关系

组件 作用 是否定制
SdMmcPciHcDxe SD Host Controller驱动 标准 + BayhubHost扩展
SdDxe SD块设备驱动 标准
NvmExpressDxe NVMe驱动 标准
PciBusDxe PCI总线枚举 标准

8.3 后续优化方向

  • 异步模式切换,减少启动时间
  • 支持更多SD Express卡型号
  • 错误恢复机制完善

这种方案已在实际嵌入式产品中验证,能够稳定地将SD Express卡作为系统启动盘使用。