mctpd是openUBMC提供的独立的MCTP路由管理服务,提供资源协作接口进行mctp协议相关通讯。
功能简介
MCTP(Management Component Transport Protocol,管理组件传输协议)是一种独立于物理介质的协议,用于计算机系统中各部件之间的信息交互。此协议独立于底层物理总线,是一种独立于总线的“数据链路层”协议。
MCTP协议由DMTF PMCI(Distributed Management Task Force Platform Management Component Intercommunications,分布式管理任务和平台管理组件互联组)工作组定义,该协议为平台管理子系统的监控&管理功能提供访问接口,进而可通过DMTF CIM(Common Information Model,数据模型)实现对平台可管理功能的访问、传输和配置。
mctpd启动流程
mctpd启动后会监听compute组件Pmu对象的Status和bios组件SmBios对象的SmBiosStatus,二者有一个变为上电且mctpd处于下电状态,将mctpd改为上电状态,重置mctpd驱动,启动mctp自发现流程。
mctp自发现流程:
- IMU发送Prepare for Endpoint Discovery Request到BMC
- BMC处理Prepare for Endpoint Discovery Request,设置Owner eid和物理地址并返回响应
- IMU发送Endpoint Discovery Request到BMC
- BMC处理Endpoint Discovery Request并返回响应
- IMU发送Set Endpoint ID Request到BMC
- BMC处理Set Endpoint ID Request,设置BMC eid和物理地址并返回响应
- mctpd拉起任务每隔5s从IMU获取一次路由信息并更新路由表
业务组件调用mctp_lib.get_endpoint_and_transport方法创建Transport并获取Endpoint对象(如果路由存在),通过Endpoint对象的Send和Request方法发送请求。
MCTP over PCIe请求消息处理流程
MCTP over SMBus/SMBus_OEM请求消息处理流程
NOTE
以上均为Request方法的流程,如果调用的是Endpoint的Send方法,则仅会将报文发送到对端,不会去尝试获取响应。
Endpoint对象
mctpd可以被多个组件加载,PCIe物理层协议类型的Endpoint依赖MCTP bus owner和mctpd驱动保证传输的实现,使用者需要自行创建所用MessageType的EndPoint,并使用EndPoint封装的方法进行MCTP通讯;
SMBus物理层协议类型的Endpoint依赖CSR配置MctpBinding对象、Endpoint对象以及关联的Chip对象,并且需要CPLD支持SMBus通信协议;
SMBus_OEM物理层协议类型的Endpoint依赖CSR配置MctpBinding对象、Endpoint对象以及关联的Chip对象,不依赖CPLD版本。
CSR配置样例
//SMBus协议类型Endpoint CSR配置样例:
"MctpBinding_1": { //MctpBinding对象配置
"BmcSMBusEid": 8, //SMBus协议使用的BMC的Eid
"BmcSMBusPhyAddr": 16 //SMBus协议使用的BMC的物理地址
}
"Endpoint_1": { //Endpoint对象配置
"TargetEid": "${Slot} |> expr(8 + $1)", //Endpoint的EID,通常为MctpBinding的eid加上slot
"TargetPhyAddr": 73, //Endpoint的物理地址(7bit)
"MessageType": 2, //Endpoint的消息协议类型,具体支持的类型详见表“目前可创建的Endpoint”
"MediumType": 128, //Endpoint的物理层协议类型,具体支持的类型详见表“目前可创建的Endpoint”
"RefChip": "#/Chip_SmbusChip" //Endpoint关联的Chip对象
}
"Chip_SmbusChip": { //Chip对象配置
"Address": 58, //Endpoint对象对应的物理硬件的地址
"AddrWidth": 1, //硬件的地址位宽
"OffsetWidth": 1, //硬件的偏移位宽
"WriteTmout": 100, //写超时
"ReadTmout": 100, //读超时
"HealthStatus": 0 //健康状态
}
//记得在ManagementTopology对应总线下配置Chip,声明Chip位于哪路总线下
"I2cMux_9545Chan": {
"Chips": [
"Chip_SmbusChip"
]
}使用Endpoint对象进行MCTP通讯
Endpoint对象提供了Request和Send方法供组件进行MCTP通讯,其中Request方法会有返回值,Send方法不会有返回值。
当前NCSI over MCTP、NVMe over MCTP以及PLDM over MCTP都在libmgmt_protocol组件中完成了封装,建议使用封装好的方法进行通讯
NOTE
可参考《南向硬件协议库》一文进行使用。
NCSI类型的Endpoint的示例
-- context是用户上下文
-- ncsi_req_ctx是协议头信息
-- ncsi_rsp_ctx是协议头的校验项目,目前只校验PacketType
-- request_data是具体的命令参数(二进制字符串)
-- timeout是等待返回的超时时间,单位是ms
-- 需要返回值:
local ok, rsp_data_bin = pcall(endpoint.Request, endpoint, context, request_data_bin, timeout, ncsi_req_ctx, ncsi_rsp_ctx)
-- 不需要返回值:
local ok = pcall(endpoint.Send, endpoint, context, request_data_bin, timeout, ncsi_req_ctx)NVMe类型的Endpoint的示例
-- context是用户上下文
-- nvme_req_ctx是协议头信息
-- nvme_rsp_ctx是协议头的校验项目,目前只校验MsgType
-- request_data是具体的命令参数(二进制字符串)
-- timeout是等待返回的超时时间,单位是ms
-- 需要返回值:
local ok, rsp_data_bin = pcall(endpoint.Request, endpoint, context, request_data_bin, timeout, nvme_req_ctx, nvme_rsp_ctx)
-- 不需要返回值:
local ok = pcall(endpoint.Send, endpoint, context, request_data_bin, timeout, nvme_req_ctx)PLDM类型的Endpoint的示例
-- context是用户上下文
-- pldm_req_ctx是协议头信息
-- pldm_rsp_ctx是协议头的校验项目,目前只校验CommandCode
-- request_data是具体的命令参数(二进制字符串)
-- timeout是等待返回的超时时间,单位是ms
-- 需要返回值:
local ok, rsp_data_bin = pcall(endpoint.Request, endpoint, context, request_data_bin, timeout, pldm_req_ctx, pldm_rsp_ctx)
-- 不需要返回值:
local ok = pcall(endpoint.Send, endpoint, context, request_data_bin, timeout, pldm_req_ctx)Vendor Defined-PCI类型的Endpoint的示例
-- context是用户上下文
-- request_data包含了厂商自定义的协议头信息和具体的命令参数(二进制字符串)
-- timeout是等待返回的超时时间,单位是ms
-- 需要返回值:
local ok, rsp_data_bin = pcall(endpoint.Request, endpoint, context, request_data_bin, timeout, {}, {})
-- 不需要返回值:
local ok = pcall(endpoint.Send, endpoint, context, request_data_bin, timeout, {})目前可创建的Endpoint
| 名称 | MessageTypeCode | 支持的物理层类型以及MediumTypeCode | 代码中描述 |
|---|---|---|---|
| MCTP Control | 0x00 | PCIe(0x0F)、SMBus(0x02)、SMBus_OEM(0x80) | MCTP_MESSAGE_TYPE_MCTP_CTRL |
| Platform Level Data Model | 0x01 | PCIe(0x0F)、SMBus(0x02)、SMBus_OEM(0x80) | MCTP_MESSAGE_TYPE_PLDM |
| NC-SI over MCTP | 0x02 | PCIe(0x0F)、SMBus(0x02)、SMBus_OEM(0x80) | MCTP_MESSAGE_TYPE_NCSI |
| NVMe over MCTP | 0x04 | PCIe(0x0F)、SMBus(0x02)、SMBus_OEM(0x80) | MCTP_MESSAGE_TYPE_NVME |
| Vendor Defined-PCI | 0x7E | PCIe(0x0F)、SMBus(0x02)、SMBus_OEM(0x80) | MCTP_MESSAGE_TYPE_VDPCI |
其他的上层协议,可以通过创建通用MCTP Endpoint,并自行封装上层协议。
mctp_lib使用指导
为了方便创建MCTP Endpoint,mctpd提供了mctp_lib模块,方便开发者快速创建Endpoint和Transport对象。
使用方式
local mctp_lib = require 'mctp_lib'获取BMC的MCTP端口信息
-- 返回值结构体:
-- {
-- OwnerEid: IMU的Eid
-- OwnerPhyAddr: IMU的PhyAddr
-- BmcEid: bmc的eid
-- BmcPhyAddr: bmc的PhyAddr
-- DiscoverFinished: 是否完成过Endpoint Discovery
-- RoutingTableReady:是否获取到了路由表
-- }
-- 若OS未上电BMC的MCTP不通,会没有返回值
-- 如果要使用这个函数,需要在service.json中引用bmc.kepler.Systems.Mctp.MctpBinding.PCIe
local obj = mctp_lib.get_mctp_pcie_binding(bus)获取PCIe设备的phy_addr
-- 根据设备的bdf信息计算设备的phy_addr
-- 例:bdf:(0x82, 0, 0) => phy_addr:0x0082, bdf:(0x6, 0, 0) => phy_addr:0x0006
local phy_addr = mctp_lib.bdf_to_phy_addr(self.Bus, self.Device, self.Function)根据模块名、物理地址、协议类型和Position创建或获取资源树上的Endpoint和Transport对象
-- MODULE_NAME是模块名,为字符串类型的值
-- msg_type是协议类型,取值范围为当前支持的MessageTypeCode
-- 如果是PCIe协议的Endpoint则不需传入Position,如果是SMBus或SMBus_OEM协议的Endpoint则需要传入Endpoint所在的sr的GroupPosition、
-- 如果要使用这个函数,需要在service.json中引用bmc.kepler.Systems.Mctp.PCIeTransport和bmc.kepler.Systems.Mctp.PCIeEndpoint
local ok, endpoint, transport = pcall(mctp_lib.get_endpoint_and_transport, bus, MODULE_NAME, phy_addr, msg_type, position)根据物理地址和协议类型获取资源树上PCIe协议的Endpoint对象
-- msg_type是协议类型,取值范围为当前支持的MessageTypeCode
-- 如果Endpoint对象未上树,则函数会失败
-- 如果要使用这个函数,需要在service.json中引用bmc.kepler.Systems.Mctp.PCIeEndpoint
local ok, endpoint = pcall(mctp_lib.get_pcie_endpoint, bus, phy_addr, msg_type)根据物理地址、协议类型和Position获取资源树上SMBus协议的Endpoint对象
-- msg_type是协议类型,取值范围为当前支持的MessageTypeCode
-- position是SMBus协议的Endpoint所在的sr的GroupPosition
-- 如果Endpoint对象未上树,则函数会失败
-- 如果要使用这个函数,需要在service.json中引用bmc.kepler.Systems.Mctp.PCIeEndpoint
local ok, endpoint = pcall(mctp_lib.get_smbus_endpoint, bus, position, phy_addr, msg_type)常见问题
如何区分smbus的Endpoint和pcie的Endpoint
可以从资源树路径判断。PCIe的Endpoint路径为/bmc/kepler/Systems/:SystemId/Mctp/Endpoint/phyaddr/msg_type,物理地址唯一;而因为SMBus来源为CSR配置,且因为9545分路可以在同一总线下配置同样物理地址的Endpoint,因此需要添加Position作为唯一标识区分不同的Endpoint,即SMBus的Endpoint路径为/bmc/kepler/Systems/:SystemId/Mctp/Endpoint/position_phyaddr/msg_type
├── /bmc/kepler/Systems
│ └── /bmc/kepler/Systems/1
│ └── /bmc/kepler/Systems/1/Mctp
│ └── /bmc/kepler/Systems/1/Mctp/Endpoint
│ ├── /bmc/kepler/Systems/1/Mctp/Endpoint/0101020E_29
│ │ └── /bmc/kepler/Systems/1/Mctp/Endpoint/0101020E_29/4
│ ├── /bmc/kepler/Systems/1/Mctp/Endpoint/0101020F_29
│ │ └── /bmc/kepler/Systems/1/Mctp/Endpoint/0101020F_29/4
│ └── /bmc/kepler/Systems/1/Mctp/Endpoint/171
│ └── /bmc/kepler/Systems/1/Mctp/Endpoint/171/4为什么mctp报文发不通(over PCIe VDM)
这个问题涉及到许多方面,建议通过以下流程排查
1、确认IMU处于上电状态
这个可以在web端查看电源是否上电,或者看mctpd日志,关键字Power ON
2025-04-26 18:07:16.430174 mctpd NOTICE: mctp_mdb_mgmt.lua(246): mctp_mdb_mgmt: pmu status change to 1, OS Power ON2、上电后,确认IMU是否发送discovery报文到BMC
mctpd需要和IMU建立连接才能收发报文。IMU上电后会广播discovery报文,mctpd收到后会回复消息,IMU就会和BMC建立连接。可以通过日志确认建立到哪一步。关键字set owner_eid(收到discovery报文,设置IMU的eid和物理地址), set bmc_eid(收到set_ep_id报文,设置BMC的eid和物理地址), start update route table(开始从IMU获取路由信息), add routing entry(收到IMU返回的路由信息报文,设置路由信息)
2025-04-26 18:07:17.038622 mctpd NOTICE: mctp_engine.lua(272): mctp_engine: set owner_eid=8, owner_phy_addr=8
2025-04-26 18:07:17.109490 mctpd NOTICE: mctp_engine.lua(281): mctp_engine: set bmc_eid=11, bmc_phy_addr=768
2025-04-26 18:07:17.109862 mctpd NOTICE: mctp_discovery.lua(234): mctp_discovery: start update route table
2025-04-26 18:07:17.335796 mctpd NOTICE: mctp_routing_table.lua(52): mctp_routing_table: add routing entry, phys_address=150, starting_eid=9, entry_type=0收到路由信息后,mctpd会注册路由对象,资源树路径为/bmc/kepler/Systems/1/Mctp/Routing/phyaddr,如果路由里没有预期的网卡、盘的地址(phyaddr),需要排查对应的硬件是否有回应IMU的discovery报文。
└─/bmc/kepler/Systems/1/Mctp/Routing
├─/bmc/kepler/Systems/1/Mctp/Routing/150
├─/bmc/kepler/Systems/1/Mctp/Routing/173
└─/bmc/kepler/Systems/1/Mctp/Routing/33、上电后,确认调用方组件有注册Transport
上电后,确认调用方组件有注册Transport:且Transport的地址在路由信息内network_adapter、storage等组件会通过mctpd提供的接口注册Transport对象,mctpd会根据Transport的物理地址是否在路由内决定是否创建Endpoint对象,没有对应地址的路由信息,mctpd就不会根据Transport对象创建Endpoint对象。Transport的路径为/bmc/kepler/Systems/1/Mctp/Transport/phyaddr/msg_type,
├─/bmc/kepler/Systems
│ └─/bmc/kepler/Systems/1
│ ├─/bmc/kepler/Systems/1/Mctp
│ │ └─/bmc/kepler/Systems/1/Mctp/Transport
│ │ └─/bmc/kepler/Systems/1/Mctp/Transport/150
│ │ ├─/bmc/kepler/Systems/1/Mctp/Transport/150/1
│ │ └─/bmc/kepler/Systems/1/Mctp/Transport/150/2Endpoint创建的日志关键字:add eid info、create endpoint, 可以通过module_name确定是哪个组件要注册ep,对应的物理地址等信息也会记录在日志里
2025-04-26 18:08:01.820337 mctpd NOTICE: mctp_eid_table.lua(81): mctp_eid_table: add eid info, module_name=network_adapter,msg_src_eid=9,phy_addr=150,msg_type=1, medium_type=15, ep_status=1
2025-04-26 18:08:01.822045 mctpd NOTICE: init.lua(86): mctp_endpoints: create endpoint, phy_addr=150, eid=9, msg_type=1, medium_type=15
2025-04-26 18:08:01.980190 mctpd NOTICE: mctp_eid_table.lua(81): mctp_eid_table: add eid info, module_name=network_adapter,msg_src_eid=9,phy_addr=150,msg_type=2, medium_type=15, ep_status=1
2025-04-26 18:08:01.986384 mctpd NOTICE: init.lua(86): mctp_endpoints: create endpoint, phy_addr=150, eid=9, msg_type=2, medium_type=154、如果上面的关键点都排查了(确定上电,有路由,有Endpoint),但还是发不通,排查消息在链路的那一层断掉
PCIe协议的Endpoint消息链路为BMC->mctp驱动->IMU->Endpoint,若需要澄清非BMC问题,需要在BMC发往驱动前加下日志打印,确定BMC将报文给了驱动。
NOTE
5、协议编码本身问题
如果加上打印后发现报文收发都有打印,但是BMC还是报请求超时,则需要结合日志具体分析响应报文是否存在结构上的问题导致校验不通过,被mctpd静默丢弃。
一些常见的问题定位日志关键字:
nvme_protocol: unmatch crcncsi_protocol: check sumncsi_protocol: incorrect payload sizemctp_protocol: unexpected fragment sizemctp_protocol: sequence numbersm_bus_protocol: incorrect packet sizesm_bus_protocol: header error, wrsm_bus_protocol: incorrect payload size, data.sizepcie_vdm_protocol: vdm header error, vdm_codepcie_vdm_protocol: incorrect vdm header, payload_lenpcie_vdm_protocol: incorrect payload sizepcie_vdm_protocol: incorrect packet size