部件驱动自发现机制设计
更新时间: 2026/08/20
在Gitcode上查看源码

部件驱动自发现是南向硬件管理中,根据槽位在位状态自动识别部件、选择 CSR 并加载对应驱动 SO 的机制。本文面向驱动适配者和南向框架维护者,说明发现触发、SR 选择、热插拔卸载与排障方法。

认识部件驱动自发现

运行时入口在 Connector,驱动匹配与 dlopen 在 devmon。代码以 component_drivers 仓的 Connector、Scanner、EEPROM 以及 include/devmon/driver_abi.h 为准。运行时不按 PCIe VID 或 DID 搜索 DDS 文件来加载驱动。

适配步骤见《部件驱动适配指南》;总线与芯片接线见《总线芯片驱动开发指南》。

说明

本文只写发现触发、SR 选择与加载路径。DDS 代码生成与出包步骤见各品类适配指南。

目录

  1. 范围与术语
  2. 端到端时序
  3. 触发条件
  4. SR 选择
  5. EEPROM 与 LoadStatus
  6. 框架侧加载
  7. 适配检查清单
  8. 故障排查
  9. 附录(对照源码)

范围与术语

底板或上级板的 CSR 先实例化槽位 Connector。Connector 在部件在位后交出一份 SR JSON,devmon 再按 SR 里的 Unit.Compatible 加载对应 SO,并执行 ctorinitstart

仓边界

组件职责
发现入口component_drivers 的 libConnector.so监听在位、选择 SR、调用 Devmon。
在位采集Scanner、Accessor把硬件在位或 BoardId 同步到属性。
身份与 SR 原文EEPROM提供 GetSRData、头解析与验签。
解析与加载devmon选择 CSR、执行 dlopen、建对象树、卸载对象。
业务驱动各品类 SO被加载后实现网卡、GPU、电源等接口。

术语

术语含义
CSR部件自描述的总称。运行时加载的是 SR JSON(来自文件或 EEPROM)。
SRService Record。扩展名为 .sr,或位于 EEPROM 内的 JSON,含 UnitObjects,常含 ManagementTopology
DDSDevice Description Schema(.dds)。定义对象路径与接口,用于代码生成,不作为运行时驱动匹配键。
Connector槽位对象。Presence 为 1 时加载下级 SR,为 0 时卸载。
PresenceConnector 在位。取值为常量 1,或 #/Scanner_xxx.Value 等引用。
IdentifyMode下级身份识别方式。代码里只有 3 走 EEPROM;1 与 2 走本地 .sr
CompatibleSR 的 Unit.Compatible[]。按序尝试 lib${name}.so,逗号会先换成下划线。
CSR1 与 CSR2SR 顶层 FormatVersion 小于 5.0 走 CSR1 的 device_loader;大于或等于 5.0 走 CSR2 的 device_manager
LoadStatusConnector 加载结果。0 表示成功,255 表示默认或已卸载,其余为错误码。

DDS 文件名规则(如 14140130_[VID][DID]_[SVID][SSID].dds)见各品类适配指南与《部件驱动接口模型设计》,本文不把它当成发现算法。


端到端时序

启动到首次加载

discovery_service 在 hwproxy 就绪后调用 discovery::full_discovery:先加载 platform.sr(position 为 "00"),再加载 root.sr"01",带 SystemId=1)。CSR 文件在镜像、/data/opt/bmc/sr/gold/data/opt/bmc/sr/temp 之间按 DataVersion 择优。

root SR 描述底板拓扑,包括 I2C、MUX、EEPROM、Scanner、Accessor 与槽位 Connector。每个对象同样经 Compatible 加载对应 SO。Connector 的 start() 只做两件事:预热 SR 缓存、检查初始 Presence。

热插拔

  • 插入Presence.changed 变为 1 后调用 try_load_device()。同一 GroupPosition 已存在时,devmon 跳过重复创建。
  • 拔出:变为 0 后调用 RemoveDevice,并置 m_require_eeprom_refresh_on_next_load 为 true。下次插入跳过 Connector 缓存,重新读 Chip。
  • 拔出时不会 dlclose 业务 SO。工厂缓存驱动句柄;卸载的是设备对象,不是 .so 文件。

下级 SR 里可以再定义 Connector,形成主板到背板、再到硬盘或电源的递归发现。


触发条件

实现位于 component_drivers 仓的 drivers/connector/connector/connector.cpp(生命周期)与 interface/i_connector.cpp(信号与加载)。

Connector 生命周期

阶段做什么不做什么
initfrom_variant 灌入 CSR,并订阅 Presence.property_changed()不读 EEPROM,不调用 AddDevice。
start调用 preload_sr_cache();若 Presence 为 1,则 mc::runtime::post(try_load_device)preload 失败不改 LoadStatus
stop清 SR 缓存;下次必须重读 EEPROM。无。

对象表中必须有名为 Devmon 的对象,否则 try_load_deviceRemoveDevice 会打错误日志后返回。调用的方法是 AddDeviceWithData(sr_data, connector_dict)RemoveDevice(connector_dict)

Connector 对象层还有未出现在 bmc.kepler.Connector 接口上的属性:Chip(EEPROM 引用)、ContainerPositionIdChipAddrCSRVersion。IdentifyMode 为 3 时靠 Chip 调用 GetSRData

Presence 来源

CSR 示例:

json
"Presence": "#/Scanner_BoardPresence.Value"
来源行为适用
常量 1start() 时就会异步加载。永远在位的部件。
#/Scanner_xxx.ValueScanner 周期读 Chip 的 BitIO 或 BlockIO,防抖后执行 Value.set_value热插拔(推荐)。
Accessor 的 Value读属性才访问硬件,无定时器。更适合绑定 Id,不适合作为热插拔 Presence。

CSR2 Scanner(drivers/scanner/)按 Period 毫秒扫描。CSR1 Scanner(drivers/csr1/scanner/)额外有:

  • 默认约 30s 启动延迟(环境变量 SCANNER_PERIOD_DELAY_TIME);
  • SuccessDebounceCountFailureDebounceCount(默认 10)满足后才改 Value
  • 同芯片偏移可合并,只让一个 master 读硬件。

说明

把 Presence 绑到 CSR1 Scanner 时,Connector 的 start() 那一刻 Presence 常常仍为 0。真正加载发生在 Scanner 第一次把 Value 写成 1 之后。

Reload 接口

接口为 bmc.kepler.Connector.Reload(Bom, Id, AuxId, IdentifyMode)

  1. 若当前 LoadStatus 为 SUCCESS,先 RemoveDevice
  2. 写入非空的 Bom、Id、AuxId 和新的 IdentifyMode;
  3. 清 Connector 缓存与本地 csr_version.json 缓存,并置 refresh;
  4. 最多两次 try_load_device

用于改识别方式或强制换一份 SR,而不依赖拔插。


SR 选择

实现为 IConnector::resolve_sr_data。单测见 tests/drivers/connector/test_connector.cpp

本地文件名

目录默认 /opt/bmc/sr,可用环境变量 CONNECTOR_SR_DIR 覆盖。

text
{dir}/{Bom}_{Id}_{AuxId}.sr

缺 Bom 或缺 AuxId 则少对应段。版本表 {dir}/csr_version.json 用去掉扩展名的文件名作 key,读取 DataVersion

IdentifyMode

规格含义代码行为
1BoardId 可读不读 EEPROM。用 CSR 里的 Bom、Id、AuxId 拼本地 .sr。Id 常由 #/Accessor_xxx.Value 注入。
2BoardId 不可读(由配置上报)同上,走本地文件。
3EEPROM 自描述读 Chip 的 BoardId 与 SR,再和本地比版本。

常量为 identify_mode::EEPROM = 3。其它值全部走本地文件。

IdentifyMode 为 3 时的决策

  • 快路径:CSR2 的 eeprom_object::start() 已解析 128 字节头并填好 BoardIdDataVersion 时,Connector 先用属性做身份和版本比较,需要 payload 时再 GetSRData(通常打中芯片缓存)。
  • 慢路径:CSR1 EEPROM 的 start() 不读硬件,BoardId 为空,必然走到 GetSRData。先走 CSR1 接口,失败再走 CSR2。
  • version_compare(a, b) 表示 a 大于或等于 b(major.minor)。相等也选 EEPROM。本地严格更新则用本地内容,但对外 DataVersion 写成 EEPROM 的版本。
  • soft 合并仅针对 EEPROM 源,且 FormatVersion 在 3.0(含)到 5.0(不含)之间。同名子字典以 soft 为底、EEPROM 覆盖。合并失败仍返回未合并的 EEPROM 数据。大于或等于 5.0 视为设备树 CSR,不再拆 soft。

注意

芯片返回硬件访问失败、验签失败或格式错误时,禁止降级到可能属于另一块板的本地文件。仅当完成码为成功但身份为空时,才使用 CSR 里配置的 Id。

三层缓存

位置命中条件失效
芯片内存chip_eeprom::m_sr_cache签名区一致,且 BoardId 与版本一致。头解析失败,或调用 invalidate_sdr_cache
芯片磁盘/data/opt/bmc/sr/backup/{uid}.bin文件尾部签名与当前签名区一致。签名变化。
ConnectorIConnector::m_sr_cache来源、board_id、两个版本与文件路径一致。Presence 为 0、Reload,或 refresh 标志。

m_require_eeprom_refresh_on_next_load 初始为 true;preload 成功置 false;拔出或 Reload 再置 true。为 true 时跳过 Connector 缓存。缓存命中且 IdentifyMode 为 3 时,正式 load 可以零次访问 Chip。


EEPROM 与 LoadStatus

设备树入口:

  • CSR2:bmc.dev.Chip.SelfDescriptionRecord.GetSRData 返回 (uint8_t status, string json),同时刷新 FormatVersionDataVersionBoardId
  • CSR1:bmc.kepler.EepromData.GetSRData 返回 (status, json, uid)

内部均落到 chip_eeprom::get_sdr_datadrivers/internal/chip/chip_eeprom/eeprom.cpp)。访问前锁 host_bus。

头与流水线

EEPROM 偏移以 8 字节步长计。开头 128 字节含 UID、sign_offsetcsr_offset、CRC32。由此得到:

  • BoardId 与 CSR1 的 uid 等于头内 24 字节 UID 字符串;
  • FormatVersion 等于 {ver}.00
  • DataVersion 等于 {sr_version_high}.{sr_version_low:02}

get_sdr_data 顺序:解析头,读签名区,查内存或磁盘缓存,读被签名区。签名区非全 0 则做 ECDSA-SHA256 验签(先内置 P-256 公钥,失败再读 /opt/bmc/trust/partner/device_desc_pubkey.bin),再从 csr_offset 解 gzip JSON。签名全 0 视为老硬件无签名。部分遗留 UID 在白名单内,签名参数非法或验签失败也可放行(不备份)。

解压失败返回完成码 4(获取 SR 失败),此时 Connector 才允许转本地 CSR。页读写与 CRC 实现细节见 EEPROM 源码,本文不展开。

LoadStatus

与 EEPROM 完成码对齐,由 Connector 在正式 load 时写入(preload 失败不改):

名称典型原因
0SUCCESSAddDeviceWithData 成功。
1HARDWARE_ACCESS_FAILI2C、总线或前级 MUX 失败。
2SIGNATURE_VERIFY_ERROR验签失败且不在白名单。
3DATA_FORMAT_ERROR头 CRC 失败、非 JSON 或 gzip,或本地 JSON 损坏。
4LOCAL_FILE_ERROR.sr 不存在或读失败。
5OBJECT_PARSE_ERROR预留。
6INTEGRITY_VERIFY_ERROR预留。
7FORMAT_VERSION_ERROR预留。
255DEFAULT初始、已 Remove,或 Reload 清状态。

try_load_device 在拿到 SR 后调用 AddDevice;invoke 抛异常时打错误日志,不一定改写 LoadStatus(成功路径才置 0)。拿不到 SR 时把上述错误码写到 LoadStatus


框架侧加载

Connector 交出 SR 之后的工作在 devmon。FormatVersion 小于 5.0 走 CSR1 的 device_loader;否则走 CSR2 的 device_manager::add_device

从 Compatible 到 SO

  • Compatible: ["hisi_182x"] 对应 libhisi_182x.so
  • Compatible: ["Mellanox,CX5"] 对应 libMellanox_CX5.so
  • CSR1 还会尝试 libCsr1${name}.so
  • 列表从前到后,第一个加载成功的即停;全部失败则解析失败,设备不会 start。
  • SO 候选来自驱动安装目录与 /data/opt/bmc/drivers/goldtempflash_driver_store)。实现细节以 devmon 为准。

Unit.Type 必须等于该 SO 注册的某个 device_name(如 PCIeNicCard)。对象实例名来自 Unit.Name,并可按 Connector 的 position 重命名以保证全局唯一。

ManagementTopology

SR 的 ManagementTopology 把模板总线名映射到 Connector 字典里的 Buses(字符串数组,元素是底板已有总线对象名)。devmon 抽出可达的 Bus 与 Chip,在 device_manager(component_drivers 的 libInternal.so)里创建内部对象并接线 left_bushost_busleft_chip。设备树 SO 的 init() 不创建内部芯片,只按 object_name 执行 find_object。EEPROM 能 GetSRData 的前提是这条拓扑已经接好。详见《总线芯片驱动开发指南》。

ABI

以 component_drivers 仓的 include/devmon/driver_abi.h 为准。每个 SO 必须用 extern "C" 导出 register_device_driver

cpp
typedef enum status {
    STATUS_OK              = 0,
    STATUS_ERROR           = 1,
    STATUS_NOT_FOUND       = 2,
    STATUS_INVALID_ARG     = 3,
    STATUS_NOT_IMPLEMENTED = 4,
    STATUS_TIMEOUT         = 5,
    STATUS_BUSY            = 6,
    STATUS_NO_MEMORY       = 7,
} status_t;

struct device_driver {
    const char*        device_name;
    const driver_ctor  ctor;   // (service, object_name) -> handle
    const driver_init  init;   // (handle, csr_object, connector)
    const driver_start start;
    const driver_stop  stop;
    driver_dump        dump;   // 可为 nullptr
};

一个 SO 可注册多种 device_name。以 drivers/pcie_nic_card/hisi/hi182x/hi182x_abi.cpp 为例,同一库导出 PCIeNicCardNicPortOpticalTransceiver。空指针参数返回 STATUS_ERROR,不要使用已不存在的 STATUS_INVALID_PARAM

ctor 只创建对象并设置 service 与 object_name。init 灌入 CSR、建子对象关系,不开定时器和协议。start 才启动业务。stop 停业务并清理。Connector 自身就是这个模型:init 订阅信号,start 才检查 Presence。

热插拔 RemoveDevice 对主对象调 stop,并尝试 stop 同 position 下的子类型对象,不会因此卸载 .so。若调用 device_driver::unload(),必须先清空 m_devicesdlclose,因为函数指针在库内。

属性注入

CSR 的 Objects 按接口名组织,经 MC_REFLECTfrom_variant 映射到对象属性。占位符(${Slot}#/Scanner.Value 等)由 devmon 的 CSR 解析与引擎引用机制处理,不是 Connector 里的独立函数。Connector 传给框架的字典字段包括:ObjectNameBomSlotGroupIdGroupPositionPresenceIdAuxIdBusesSystemIdManagerIdSilkTextTypeIdentifyModeChassisIdLoadStatusContainerChip。缺省时用默认值,避免类型转换失败。


适配检查清单

驱动写完却加载不上时按此核对,不必先怀疑 VID 或 DID 扫描。

底板或上级 CSR:

  • [ ] 有对应槽位的 Connector 对象,且 Compatible 能加载到 libConnector.so
  • [ ] Presence 有来源:常量 1,或指向已创建的 Scanner 的 Value
  • [ ] IdentifyMode 与板类型一致:EEPROM 自描述用 3;否则保证本地 .sr 的 Bom、Id、AuxId 能拼出文件。
  • [ ] IdentifyMode 为 3 时,Chip 指向已接线的 EEPROM 对象(host_busleft_bus 非空)。
  • [ ] Buses 列出下级 SR 拓扑要用到的底板总线名。
  • [ ] GroupPosition 在同一 CSR 内不重复。

下级 SR 与驱动 SO:

  • [ ] 文件在 /opt/bmc/sr/(或 gold、temp、EEPROM);IdentifyMode 不是 3 时,csr_version.json 有对应条目。
  • [ ] Unit.Type 等于 SO 里某个 device_name
  • [ ] Unit.Compatible 能落到已安装的 libxxx.so(注意逗号转下划线)。
  • [ ] Objects 接口名与 MC_REFLECT、DDS 生成的 gen:: 接口一致。
  • [ ] 需要热插拔时,不要把 Presence 只绑在无定时器的 Accessor 上。

品类目录、DDS 命名与出包步骤仍以《网卡驱动适配指南》至《风扇电源驱动适配指南》为准。


故障排查

按 LoadStatus

LoadStatus先查
255 且部件已在位Presence 是否从未变成 1(Scanner 延迟或防抖);对象表有无 Devmon。
1I2C、MUX、EEPROM 总线;Chip 引用是否为空。
2验签公钥、是否老 UID 白名单、签名区是否损坏。
3EEPROM 头 CRC、gzip 或 JSON;或本地 .sr 不是合法 JSON。
4/opt/bmc/sr/{Bom}_{Id}_{AuxId}.sr 是否存在;Id 是否已从 EEPROM 改写导致文件名变化。
0 但业务对象没有AddDevice 后 Compatible 与 Unit.Type 是否匹配;查看 devmon 侧加载日志。

日志关键字

  • [self-discovery][self-release]:Presence 边沿。
  • SR source=EEPROMSR source=LOCAL:最终选用哪份数据。
  • merge soft data:soft 合并是否成功。
  • Devmon.AddDeviceWithData failed:框架解析或建对象失败(此时 SR 已选出)。

Connector dump

LoadStatus 为 SUCCESS 时,Connector::dump() 在标准属性(. 前缀)之外输出 - 前缀字段,包括 SourcePathSoftSourcePathDataSourceEEPROMLOCALNA),以及有效、EEPROM、本地的 FormatVersion 与 DataVersion。hwdiscovery 的 discovery dump 会汇总各 Connector,并补 flash 副本完整性字段。

注意

不要用主机上的 lspcii2cdetect 作为 BMC 南向自发现的诊断手段。


附录(对照源码)

路径相对于对应 Git 仓根目录。component_drivers 见 GitCode 仓库;devmon 见 GitCode 仓库

主题路径
Connector 状态机、SR 选择drivers/connector/connector/interface/i_connector.cpp
Connector ABI 与 dumpdrivers/connector/connector/connector.cppconnector_abi.cpp
CSR2 EEPROM 的 GetSRDatadrivers/chip/interface/i_self_description_record.cpp
头解析、验签、backupdrivers/internal/chip/chip_eeprom/eeprom.cpp
CSR1 GetSRData(start 不读硬件)drivers/csr1/chip/common/interface/i_eeprom_data.cppdrivers/csr1/chip/eeprom/eeprom.cpp
CSR2 与 CSR1 Scannerdrivers/scanner/scanner/drivers/csr1/scanner/scanner/
Accessordrivers/accessor/accessor/
ABI 头文件include/devmon/driver_abi.h
启动发现libs/hwdiscovery/src/discovery/discovery.cpp(devmon)
Add 与 Removelibs/hwdiscovery/src/discovery/hwdiscovery_root_interface.cpp(devmon)
Compatible 与拓扑libs/common/src/device/csr.cppmanager.cpp(devmon)
SO 加载与 flash 择优libs/common/src/device_driver.cppflash_driver_store.cpp(devmon)
Connector 行为单测tests/drivers/connector/test_connector.cpp