| @@ -1,5 +1,8 @@ | |||
| 1 | # Changelog | 1 | # Changelog |
| 2 | All notable changes to this project will be documented in this file. | 2 | All notable changes to this project will be documented in this file. |
| 3 | +## [1.130.21] - 2026-10-27 | ||
| 4 | +修复Control查询回包和Set EID语义,补充稳定设备UUID及按实际注册状态返回的能力查询。 | ||
| 5 | + | ||
| 3 | ## [1.130.15] - 2026-08-25 | 6 | ## [1.130.15] - 2026-08-25 |
| 4 | 更新版本号,触发流水线推包 | 7 | 更新版本号,触发流水线推包 |
| 5 | 8 | ||
| @@ -2,7 +2,7 @@ | |||
| 2 | "name": "mctpd", | 2 | "name": "mctpd", |
| 3 | "type": "application", | 3 | "type": "application", |
| 4 | "deployConfig": "framework.service", | 4 | "deployConfig": "framework.service", |
| 5 | - "version": "1.130.15", | 5 | + "version": "1.130.21", |
| 6 | "author": "Huawei", | 6 | "author": "Huawei", |
| 7 | "license": "Mulan PSL v2", | 7 | "license": "Mulan PSL v2", |
| 8 | "options": { | 8 | "options": { |
| @@ -15,12 +15,14 @@ local c_tasks = require('mc.orm.tasks') | |||
| 15 | local signal = require('mc.signal') | 15 | local signal = require('mc.signal') |
| 16 | local network = require('network.core') | 16 | local network = require('network.core') |
| 17 | local mctp_def = require('mctp_def') | 17 | local mctp_def = require('mctp_def') |
| 18 | +local context = require('mc.context') | ||
| 19 | +local vos = require('utils.vos') | ||
| 18 | 20 | ||
| 19 | local MCTP_CMD_SET_ENDPOINT_ID <const> = 0x01 | 21 | local MCTP_CMD_SET_ENDPOINT_ID <const> = 0x01 |
| 20 | local MCTP_CMD_GET_ENDPOINT_ID <const> = 0x02 | 22 | local MCTP_CMD_GET_ENDPOINT_ID <const> = 0x02 |
| 21 | --- local MCTP_CMD_GET_ENDPOINT_UUID <const> = 0x03 | 23 | +local MCTP_CMD_GET_ENDPOINT_UUID <const> = 0x03 |
| 22 | --- local MCTP_CMD_GET_MCTP_VERSION_SUPPORT <const> = 0x04 | 24 | +local MCTP_CMD_GET_MCTP_VERSION_SUPPORT <const> = 0x04 |
| 23 | --- local MCTP_CMD_GET_MESSAGE_TYPE_SUPPORT <const> = 0x05 | 25 | +local MCTP_CMD_GET_MESSAGE_TYPE_SUPPORT <const> = 0x05 |
| 24 | -- local MCTP_CMD_GET_VENDOR_DEFINED_MESSAGE_SUPPORT <const> = 0x06 | 26 | -- local MCTP_CMD_GET_VENDOR_DEFINED_MESSAGE_SUPPORT <const> = 0x06 |
| 25 | -- local MCTP_CMD_RESOLVE_ENDPOINT_ID <const> = 0x07 | 27 | -- local MCTP_CMD_RESOLVE_ENDPOINT_ID <const> = 0x07 |
| 26 | -- local MCTP_CMD_ALLOCATE_ENDPOINT_IDS <const> = 0x08 | 28 | -- local MCTP_CMD_ALLOCATE_ENDPOINT_IDS <const> = 0x08 |
| @@ -36,9 +38,9 @@ local MCTP_CMD_DISCOVERY_NOTIFY <const> = 0x0D | |||
| 36 | local MCTP_CTRL_CC_SUCCESS <const> = 0x00 | 38 | local MCTP_CTRL_CC_SUCCESS <const> = 0x00 |
| 37 | -- local MCTP_CTRL_CC_ERROR <const> = 0x01 | 39 | -- local MCTP_CTRL_CC_ERROR <const> = 0x01 |
| 38 | local MCTP_CTRL_CC_ERROR_INVALID_DATA <const> = 0x02 | 40 | local MCTP_CTRL_CC_ERROR_INVALID_DATA <const> = 0x02 |
| 39 | --- local MCTP_CTRL_CC_ERROR_INVALID_LENGTH <const> = 0x03 | 41 | +local MCTP_CTRL_CC_ERROR_INVALID_LENGTH <const> = 0x03 |
| 40 | --- local MCTP_CTRL_CC_ERROR_NOT_READY <const> = 0x04 | 42 | +local MCTP_CTRL_CC_ERROR_NOT_READY <const> = 0x04 |
| 41 | --- local MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD <const> = 0x05 | 43 | +local MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD <const> = 0x05 |
| 42 | 44 | ||
| 43 | local MCTP_DYNAMIC_EID <const> = 0 | 45 | local MCTP_DYNAMIC_EID <const> = 0 |
| 44 | -- local MCTP_STATIC_EID <const> = 1 | 46 | -- local MCTP_STATIC_EID <const> = 1 |
| @@ -46,7 +48,6 @@ local MCTP_DYNAMIC_EID <const> = 0 | |||
| 46 | local ROUTE_TO_ROOT <const> = 0x00 | 48 | local ROUTE_TO_ROOT <const> = 0x00 |
| 47 | local ROUTE_BY_ID <const> = 0x02 | 49 | local ROUTE_BY_ID <const> = 0x02 |
| 48 | 50 | ||
| 49 | -local MCTP_EID_NULL <const> = 0 | ||
| 50 | local MCTP_EID_BROADCAST <const> = 0xFF | 51 | local MCTP_EID_BROADCAST <const> = 0xFF |
| 51 | 52 | ||
| 52 | local MCTP_REQUEST_DEFAULT_TIMEOUT = 15000 -- 默认超时时间 15 秒 | 53 | local MCTP_REQUEST_DEFAULT_TIMEOUT = 15000 -- 默认超时时间 15 秒 |
| @@ -78,6 +79,17 @@ function c_mctp_discovery:init() | |||
| 78 | end | 79 | end |
| 79 | -- 启动set eid失败轮询任务,周期性重发失败ep列表中的设置eid报文 | 80 | -- 启动set eid失败轮询任务,周期性重发失败ep列表中的设置eid报文 |
| 80 | self:start_smbus_eid_poll_task() | 81 | self:start_smbus_eid_poll_task() |
| 82 | + if self.mctp_engine.bus then | ||
| 83 | + self.identity_task = c_tasks | ||
| 84 | + .get_instance() | ||
| 85 | + :new_task('mctp_identity' .. self.system_id) | ||
| 86 | + :loop(function(task) | ||
| 87 | + if self.endpoint_uuid or self:load_endpoint_identity() then | ||
| 88 | + task:stop() | ||
| 89 | + end | ||
| 90 | + end) | ||
| 91 | + :set_timeout_ms(1000) | ||
| 92 | + end | ||
| 81 | 93 | ||
| 82 | self.on_discovery_changed:on(function(status) | 94 | self.on_discovery_changed:on(function(status) |
| 83 | if status then | 95 | if status then |
| @@ -89,6 +101,100 @@ function c_mctp_discovery:init() | |||
| 89 | end) | 101 | end) |
| 90 | end | 102 | end |
| 91 | 103 | ||
| 104 | +function c_mctp_discovery:set_endpoint_identity(identity) | ||
| 105 | + if type(identity) ~= 'string' or (#identity ~= 32 and #identity ~= 36) then | ||
| 106 | + return false | ||
| 107 | + end | ||
| 108 | + local name = identity:gsub('-', ''):lower() | ||
| 109 | + if #name ~= 32 or name:find('[^%x]') or name == string.rep('0', 32) or name == string.rep('f', 32) then | ||
| 110 | + return false | ||
| 111 | + end | ||
| 112 | + -- 用掉电持久化设备UUID派生RFC 4122 v5;不使用可改的SystemGUID或动态EID。 | ||
| 113 | + local dns_namespace = '\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8' | ||
| 114 | + local digest = vos.compute_checksum(vos.G_CHECKSUM_SHA1, dns_namespace .. 'openubmc:mctp:' .. name) | ||
| 115 | + local bytes = {} | ||
| 116 | + for i = 1, 16 do | ||
| 117 | + bytes[i] = tonumber(digest:sub(i * 2 - 1, i * 2), 16) | ||
| 118 | + end | ||
| 119 | + bytes[7], bytes[9] = (bytes[7] & 0x0f) | 0x50, (bytes[9] & 0x3f) | 0x80 | ||
| 120 | + local uuid = string.char(table.unpack(bytes)) | ||
| 121 | + if self.endpoint_uuid and self.endpoint_uuid ~= uuid then | ||
| 122 | + return false | ||
| 123 | + end | ||
| 124 | + self.endpoint_uuid = uuid | ||
| 125 | + return true | ||
| 126 | +end | ||
| 127 | + | ||
| 128 | +function c_mctp_discovery:load_endpoint_identity() | ||
| 129 | + if self.endpoint_uuid then | ||
| 130 | + return true | ||
| 131 | + end | ||
| 132 | + local stage = 'GetSubObjects' | ||
| 133 | + local ok, loaded = pcall(function() | ||
| 134 | + local bus, iface = self.mctp_engine.bus, 'bmc.kepler.Assembly' | ||
| 135 | + local objects = bus:timeout_call( | ||
| 136 | + 100, | ||
| 137 | + 'bmc.kepler.maca', | ||
| 138 | + '/bmc/kepler/MdbService', | ||
| 139 | + 'bmc.kepler.Mdb', | ||
| 140 | + 'GetSubObjects', | ||
| 141 | + 'a{ss}sias', | ||
| 142 | + context.new(), | ||
| 143 | + '/bmc/kepler/Managers', | ||
| 144 | + 0, | ||
| 145 | + { iface } | ||
| 146 | + ) | ||
| 147 | + stage = 'unique Assembly owner' | ||
| 148 | + local path, owners = next(objects) | ||
| 149 | + assert(path and not next(objects, path), 'missing or ambiguous device identity') | ||
| 150 | + local service = next(owners) | ||
| 151 | + assert(service and not next(owners, service), 'ambiguous device identity owner') | ||
| 152 | + stage = 'GetAllWithContext' | ||
| 153 | + local values = bus.bus | ||
| 154 | + :timeout_call( | ||
| 155 | + 100, | ||
| 156 | + service, | ||
| 157 | + path, | ||
| 158 | + 'bmc.kepler.Object.Properties', | ||
| 159 | + 'GetAllWithContext', | ||
| 160 | + 'a{ss}s', | ||
| 161 | + context.new(), | ||
| 162 | + iface | ||
| 163 | + ) | ||
| 164 | + :read() | ||
| 165 | + stage = 'persistent UUID format' | ||
| 166 | + return self:set_endpoint_identity(values.UUID and values.UUID:value()) | ||
| 167 | + end) | ||
| 168 | + if (not ok or not loaded) and not self.identity_warned then | ||
| 169 | + self.identity_warned = true | ||
| 170 | + log:notice('[System%s]MCTP endpoint identity is not ready at %s; retrying', self.system_id, stage) | ||
| 171 | + end | ||
| 172 | + return ok and loaded == true | ||
🔵 Low Priority 新增的 证据链:该函数是 失败模式:只要该调用面与真实框架有偏差, 建议:把 pcall 的第二个返回值保存下来( ![]() ![]() Incorrect? | |||
| 173 | +end | ||
| 174 | + | ||
| 175 | +function c_mctp_discovery:has_pldm_responder() | ||
| 176 | + -- 注册表由独立PLDM responder提供;未安装/启用该组件时不宣告PLDM。 | ||
| 177 | + local responder = self.mctp_engine.pldm_responder | ||
| 178 | + return responder ~= nil and type(responder.reg_count) == 'number' and responder.reg_count > 0 | ||
| 179 | +end | ||
| 180 | + | ||
| 181 | +function c_mctp_discovery:version_response(req) | ||
| 182 | + local typ = req.req_data:byte(1) | ||
| 183 | + local versions | ||
| 184 | + -- DSP0236 1.3.1的12.6.2/12.6.3修正1.3.0排版错误;版本大端、从旧到新。 | ||
| 185 | + local compatible = '\xf1\xf0\xff\0\xf1\xf1\xff\0\xf1\xf2\xff\0' | ||
| 186 | + if typ == 0 then | ||
| 187 | + versions = compatible | ||
| 188 | + elseif typ == 255 then | ||
| 189 | + versions = compatible .. '\xf1\xf3\xf1\0' | ||
| 190 | + elseif typ == 1 and self:has_pldm_responder() then | ||
| 191 | + versions = '\xf1\xf0\xf0\0' -- DSP0241绑定版本,不是PLDM Base/BIOS版本。 | ||
| 192 | + end | ||
| 193 | + local data = versions and string.char(#versions // 4) .. versions or '' | ||
| 194 | + self.ep:response(req, versions and MCTP_CTRL_CC_SUCCESS or 0x80, ROUTE_BY_ID, data) | ||
| 195 | + return true | ||
| 196 | +end | ||
| 197 | + | ||
| 92 | function c_mctp_discovery:update_os_power_status(is_power_on) | 198 | function c_mctp_discovery:update_os_power_status(is_power_on) |
| 93 | self.power_on = is_power_on | 199 | self.power_on = is_power_on |
| 94 | if is_power_on then | 200 | if is_power_on then |
| @@ -105,8 +211,13 @@ function c_mctp_discovery:set_discovered(status) | |||
| 105 | end | 211 | end |
| 106 | end | 212 | end |
| 107 | 213 | ||
| 214 | +local REQUEST_LENGTH = { [1] = 2, [2] = 0, [3] = 0, [4] = 1, [5] = 0, [6] = 1, [11] = 0, [12] = 0 } | ||
| 108 | function c_mctp_discovery:control_rx_handler(msg) | 215 | function c_mctp_discovery:control_rx_handler(msg) |
| 109 | local cmd = msg.command_code | 216 | local cmd = msg.command_code |
| 217 | + if REQUEST_LENGTH[cmd] and #msg.req_data ~= REQUEST_LENGTH[cmd] then | ||
| 218 | + self.ep:response(msg, MCTP_CTRL_CC_ERROR_INVALID_LENGTH, ROUTE_BY_ID) | ||
| 219 | + return true | ||
| 220 | + end | ||
| 110 | 221 | ||
| 111 | if cmd == MCTP_CMD_PREPARE_FOR_ENDPOINT_DISCOVERY then | 222 | if cmd == MCTP_CMD_PREPARE_FOR_ENDPOINT_DISCOVERY then |
| 112 | return self:process_prepare_for_ep_discovery_req(msg) | 223 | return self:process_prepare_for_ep_discovery_req(msg) |
| @@ -116,8 +227,22 @@ function c_mctp_discovery:control_rx_handler(msg) | |||
| 116 | return self:response(msg) | 227 | return self:response(msg) |
| 117 | elseif cmd == MCTP_CMD_SET_ENDPOINT_ID then | 228 | elseif cmd == MCTP_CMD_SET_ENDPOINT_ID then |
| 118 | return self:process_set_ep_id_req(msg) | 229 | return self:process_set_ep_id_req(msg) |
| 230 | + elseif cmd == MCTP_CMD_GET_ENDPOINT_UUID then | ||
| 231 | + self.ep:response( | ||
| 232 | + msg, | ||
| 233 | + self.endpoint_uuid and MCTP_CTRL_CC_SUCCESS or MCTP_CTRL_CC_ERROR_NOT_READY, | ||
| 234 | + ROUTE_BY_ID, | ||
| 235 | + self.endpoint_uuid | ||
| 236 | + ) | ||
| 237 | + return true | ||
| 238 | + elseif cmd == MCTP_CMD_GET_MCTP_VERSION_SUPPORT then | ||
| 239 | + return self:version_response(msg) | ||
| 240 | + elseif cmd == MCTP_CMD_GET_MESSAGE_TYPE_SUPPORT then | ||
| 241 | + self.ep:response(msg, MCTP_CTRL_CC_SUCCESS, ROUTE_BY_ID, self:has_pldm_responder() and '\1\1' or '\0') | ||
| 242 | + return true | ||
| 119 | else | 243 | else |
| 120 | - return false | 244 | + self.ep:response(msg, MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD, ROUTE_BY_ID) |
| 245 | + return true | ||
| 121 | end | 246 | end |
| 122 | end | 247 | end |
| 123 | 248 | ||
| @@ -178,44 +303,53 @@ function c_mctp_discovery:process_ep_discovery_req(req) | |||
| 178 | end | 303 | end |
| 179 | 304 | ||
| 180 | function c_mctp_discovery:response(req) | 305 | function c_mctp_discovery:response(req) |
| 181 | - self.mctp_engine:response_ctrl_msg( | 306 | + self.ep:response( |
| 182 | req, | 307 | req, |
| 183 | MCTP_CTRL_CC_SUCCESS, | 308 | MCTP_CTRL_CC_SUCCESS, |
| 184 | ROUTE_BY_ID, | 309 | ROUTE_BY_ID, |
| 185 | - cmds.mctp_ctrl_resp_get_eid.pack({ | 310 | + cmds.mctp_ctrl_resp_get_eid:pack({ |
| 186 | eid = self.mctp_engine.bmc_eid, | 311 | eid = self.mctp_engine.bmc_eid, |
| 187 | eid_type = MCTP_DYNAMIC_EID, | 312 | eid_type = MCTP_DYNAMIC_EID, |
| 188 | endpoint_type = 0, | 313 | endpoint_type = 0, |
| 189 | medium_data = 0, | 314 | medium_data = 0, |
| 190 | }) | 315 | }) |
| 191 | ) | 316 | ) |
| 192 | - self:set_discovered(true) | ||
| 193 | return true | 317 | return true |
| 194 | end | 318 | end |
| 195 | 319 | ||
| 196 | local OP_SET_EID <const> = 0 | 320 | local OP_SET_EID <const> = 0 |
| 197 | local OP_FORCE_EID <const> = 1 | 321 | local OP_FORCE_EID <const> = 1 |
| 322 | +local OP_SET_DISCOVERED <const> = 3 | ||
| 198 | local SET_EID_ACCEPTED <const> = 0x0 | 323 | local SET_EID_ACCEPTED <const> = 0x0 |
| 199 | local SET_EID_REJECTED <const> = 0x1 | 324 | local SET_EID_REJECTED <const> = 0x1 |
| 200 | function c_mctp_discovery:process_set_ep_id_req(req) | 325 | function c_mctp_discovery:process_set_ep_id_req(req) |
| 201 | local cc = MCTP_CTRL_CC_ERROR_INVALID_DATA | 326 | local cc = MCTP_CTRL_CC_ERROR_INVALID_DATA |
| 202 | - local rsp = { allocation_status = 0, eid_pool_size = 0, assignment_status = SET_EID_REJECTED } | 327 | + local rsp = { |
| 328 | + allocation_status = 0, | ||
| 329 | + eid_pool_size = 0, | ||
| 330 | + assignment_status = SET_EID_REJECTED, | ||
| 331 | + eid_set = self.mctp_engine.bmc_eid, | ||
| 332 | + } | ||
| 203 | 333 | ||
| 204 | local set_eid_req = cmds.mctp_ctrl_req_set_eid:unpack(req.req_data) | 334 | local set_eid_req = cmds.mctp_ctrl_req_set_eid:unpack(req.req_data) |
| 205 | if not set_eid_req then | 335 | if not set_eid_req then |
| 206 | - rsp.eid_set = 0 | ||
| 207 | log:error('[System%s]mctp_discovery: unpack set endpoint id data failed', self.system_id) | 336 | log:error('[System%s]mctp_discovery: unpack set endpoint id data failed', self.system_id) |
| 208 | else | 337 | else |
| 209 | local eid = set_eid_req.eid | 338 | local eid = set_eid_req.eid |
| 210 | local op = set_eid_req.operation | 339 | local op = set_eid_req.operation |
| 211 | - if eid == MCTP_EID_BROADCAST or eid == MCTP_EID_NULL then | 340 | + if op == OP_SET_DISCOVERED then |
| 212 | - rsp.eid_set = self.mctp_engine.bmc_eid | 341 | + -- PCIe的Set Discovered Flag不改变EID,忽略请求中的EID值。 |
| 342 | + cc, rsp.assignment_status = MCTP_CTRL_CC_SUCCESS, SET_EID_ACCEPTED | ||
| 343 | + self:set_discovered(true) | ||
| 344 | + elseif eid < 8 or eid == MCTP_EID_BROADCAST then | ||
| 213 | log:error('[System%s]mctp_discovery: set endpoint id failed, invalid eid %d', self.system_id, eid) | 345 | log:error('[System%s]mctp_discovery: set endpoint id failed, invalid eid %d', self.system_id, eid) |
| 214 | elseif op == OP_SET_EID or op == OP_FORCE_EID then | 346 | elseif op == OP_SET_EID or op == OP_FORCE_EID then |
| 215 | rsp.eid_set = eid | 347 | rsp.eid_set = eid |
| 216 | cc = MCTP_CTRL_CC_SUCCESS | 348 | cc = MCTP_CTRL_CC_SUCCESS |
| 217 | rsp.assignment_status = SET_EID_ACCEPTED | 349 | rsp.assignment_status = SET_EID_ACCEPTED |
| 218 | - self.mctp_engine:set_bmc_eid(eid, self.ep.pcie_vdm_ctx.bmc_phy_addr) | 350 | + local transport, physical = self.ep:get_ctx_by_medium(self.ep.medium_type) |
| 351 | + self.mctp_engine:set_owner_eid(transport.src, physical.remote_phy_addr) | ||
| 352 | + self.mctp_engine:set_bmc_eid(eid, physical.bmc_phy_addr) | ||
| 219 | log:debug( | 353 | log:debug( |
| 220 | '[System%s]mctp_discovery: set bmc_eid=%d, bmc_phy_addr=%d', | 354 | '[System%s]mctp_discovery: set bmc_eid=%d, bmc_phy_addr=%d', |
| 221 | self.system_id, | 355 | self.system_id, |
| @@ -55,8 +55,9 @@ function c_ep_mctp_ctrl:response(req, cc, routing, data) | |||
| 55 | self.mctp_engine:pcie_binding_params_set() | 55 | self.mctp_engine:pcie_binding_params_set() |
| 56 | local mctp_ctx, ctx = self:get_ctx_by_medium(self.medium_type) | 56 | local mctp_ctx, ctx = self:get_ctx_by_medium(self.medium_type) |
| 57 | mctp_ctx.tag_owner = false | 57 | mctp_ctx.tag_owner = false |
| 58 | + -- 查询也可来自其他端点,响应沿本次请求返回,不改成Bus Owner地址。 | ||
| 59 | + mctp_ctx.dest = mctp_ctx.src | ||
| 58 | mctp_ctx.src = self.mctp_engine.bmc_eid | 60 | mctp_ctx.src = self.mctp_engine.bmc_eid |
| 59 | - mctp_ctx.dest = self.mctp_engine.owner_eid | ||
| 60 | 61 | ||
| 61 | if ctx == self.pcie_vdm_ctx then | 62 | if ctx == self.pcie_vdm_ctx then |
| 62 | self.pcie_vdm_ctx.routing = routing | 63 | self.pcie_vdm_ctx.routing = routing |
| @@ -67,6 +68,9 @@ function c_ep_mctp_ctrl:response(req, cc, routing, data) | |||
| 67 | end | 68 | end |
| 68 | 69 | ||
| 69 | function c_ep_mctp_ctrl:process(_, msg) | 70 | function c_ep_mctp_ctrl:process(_, msg) |
| 71 | + if msg.datagram or msg.rq ~= self.mctp_ctx.tag_owner then | ||
| 72 | + return true | ||
| 73 | + end | ||
| 70 | local cmd = msg.command_code | 74 | local cmd = msg.command_code |
| 71 | if msg.rq == false then | 75 | if msg.rq == false then |
| 72 | if not self.mctp_engine:wakeup_request(self:make_rsp_id(msg.instance_id), msg) then | 76 | if not self.mctp_engine:wakeup_request(self:make_rsp_id(msg.instance_id), msg) then |
| @@ -33,6 +33,252 @@ local function make_fake_task() | |||
| 33 | return task | 33 | return task |
| 34 | end | 34 | end |
| 35 | 35 | ||
| 36 | +TestMctpControlAdmission = {} | ||
| 37 | + | ||
| 38 | +function TestMctpControlAdmission:setup() | ||
| 39 | + local old = c_tasks.get_instance | ||
| 40 | + c_tasks.get_instance = function() | ||
| 41 | + return { | ||
| 42 | + new_task = function() | ||
| 43 | + return make_fake_task() | ||
| 44 | + end, | ||
| 45 | + } | ||
| 46 | + end | ||
| 47 | + local ok, err = pcall(function() | ||
| 48 | + self.engine = c_mctp_engine.new(nil, 1) | ||
| 49 | + self.discovery = c_mctp_discovery.new(1, self.engine) | ||
| 50 | + end) | ||
| 51 | + c_tasks.get_instance = old | ||
| 52 | + lu.assertTrue(ok, tostring(err)) | ||
| 53 | + self.engine:set_bmc_eid(11, 0x300) | ||
| 54 | + self.engine:set_owner_eid(9, 0x2000) | ||
| 55 | + self.discovery.power_on = true | ||
| 56 | + self.discovery.start_update_route_table = function() end | ||
| 57 | + self.frames = {} | ||
| 58 | + self.engine.hw_pcie_proto:set_tx_handler(function() | ||
| 59 | + return true | ||
| 60 | + end) | ||
| 61 | + self.engine.pcie_proto:set_capture_handler(function(outgoing, _, frame) | ||
| 62 | + if outgoing then | ||
| 63 | + self.frames[#self.frames + 1] = frame | ||
| 64 | + end | ||
| 65 | + end) | ||
| 66 | +end | ||
| 67 | + | ||
| 68 | +function TestMctpControlAdmission:request(command, data, source, flags, control_flags) | ||
| 69 | + local physical = require('mctp').pcie_vdm_ctx.new() | ||
| 70 | + physical.routing, physical.remote_phy_addr, physical.bmc_phy_addr = 2, 0x2100, 0x300 | ||
| 71 | + local packet = string.char(1, 11, source or 10, flags or 0xc9, 0, control_flags or 0x87, command) .. (data or '') | ||
| 72 | + local ok, err = pcall(self.engine.mctp_proto.pop, self.engine.mctp_proto, packet, physical, mctp_def.PHY_MEDIUM_PCI) | ||
| 73 | + lu.assertTrue(ok, 'Control dispatch raised: ' .. tostring(err)) | ||
| 74 | +end | ||
| 75 | + | ||
| 76 | +function TestMctpControlAdmission:test_get_eid_uses_request_route_without_discovering() | ||
| 77 | + self:request(2) | ||
| 78 | + lu.assertEquals(#self.frames, 1) | ||
| 79 | + local frame = self.frames[1] | ||
| 80 | + lu.assertEquals(frame:sub(9, 10), '\x21\x00') | ||
| 81 | + lu.assertEquals(frame:sub(13, 23), '\x01\x0a\x0b\xc1\x00\x07\x02\x00\x0b\x00\x00') | ||
| 82 | + lu.assertFalse(self.discovery.discovered) | ||
| 83 | + lu.assertEquals({ self.engine.owner_eid, self.engine.owner_phy_addr }, { 9, 0x2000 }) | ||
| 84 | +end | ||
| 85 | + | ||
| 86 | +function TestMctpControlAdmission:test_bad_length_replies_without_state_change() | ||
| 87 | + for _, command in ipairs({ 1, 2, 3, 4, 5, 6, 11, 12 }) do | ||
| 88 | + self:request(command, 'unexpected') | ||
| 89 | + local frame = self.frames[#self.frames] | ||
| 90 | + lu.assertNotNil(frame, 'no error response for command ' .. command) | ||
| 91 | + lu.assertEquals(frame:byte(20), 3) | ||
| 92 | + lu.assertFalse(self.discovery.discovered) | ||
| 93 | + lu.assertEquals(self.engine.bmc_eid, 11) | ||
| 94 | + end | ||
| 95 | +end | ||
| 96 | + | ||
| 97 | +function TestMctpControlAdmission:test_datagram_and_wrong_tag_owner_have_no_response() | ||
| 98 | + self:request(1, '\0\12', 9, 0xc9, 0xc7) | ||
| 99 | + self:request(1, '\0\12', 9, 0xc1, 0x87) | ||
| 100 | + lu.assertEquals(self.frames, {}) | ||
| 101 | + lu.assertEquals(self.engine.bmc_eid, 11) | ||
| 102 | + lu.assertFalse(self.discovery.discovered) | ||
| 103 | +end | ||
| 104 | + | ||
| 105 | +function TestMctpControlAdmission:test_set_eid_rejects_reserved_ids_and_invalid_operations() | ||
| 106 | + for _, eid in ipairs({ 0, 1, 2, 3, 4, 5, 6, 7, 255 }) do | ||
| 107 | + self:request(1, string.char(0, eid), 9) | ||
| 108 | + lu.assertEquals(self.frames[#self.frames]:byte(20), 2) | ||
| 109 | + lu.assertEquals(self.engine.bmc_eid, 11) | ||
| 110 | + lu.assertFalse(self.discovery.discovered) | ||
| 111 | + end | ||
| 112 | + self:request(1, '\x02\x0c', 9) | ||
| 113 | + lu.assertEquals(self.frames[#self.frames]:byte(20), 2) | ||
| 114 | + lu.assertEquals(self.engine.bmc_eid, 11) | ||
| 115 | +end | ||
| 116 | + | ||
| 117 | +function TestMctpControlAdmission:test_set_discovered_flag_preserves_eid() | ||
| 118 | + for _, ignored_eid in ipairs({ 0, 255, 12 }) do | ||
| 119 | + self:request(1, string.char(3, ignored_eid), 9) | ||
| 120 | + lu.assertEquals(self.frames[#self.frames]:sub(20, 23), '\x00\x00\x0b\x00') | ||
| 121 | + lu.assertTrue(self.discovery.discovered) | ||
| 122 | + lu.assertEquals(self.engine.bmc_eid, 11) | ||
| 123 | + end | ||
| 124 | +end | ||
| 125 | + | ||
| 126 | +function TestMctpControlAdmission:test_set_eid_captures_request_owner_and_replies_with_new_address() | ||
| 127 | + self:request(1, '\0\12', 9) | ||
| 128 | + lu.assertEquals(self.frames[#self.frames]:sub(13, 23), '\x01\x09\x0c\xc1\x00\x07\x01\x00\x00\x0c\x00') | ||
| 129 | + lu.assertTrue(self.discovery.discovered) | ||
| 130 | + lu.assertEquals({ self.engine.bmc_eid, self.engine.bmc_phy_addr }, { 12, 0x300 }) | ||
| 131 | + lu.assertEquals({ self.engine.owner_eid, self.engine.owner_phy_addr }, { 9, 0x2100 }) | ||
| 132 | +end | ||
| 133 | + | ||
| 134 | +function TestMctpControlAdmission:test_uuid_is_stable_derived_identity_and_not_eid() | ||
| 135 | + self:request(3) | ||
| 136 | + lu.assertEquals(self.frames[#self.frames]:byte(20), 4) | ||
| 137 | + lu.assertTrue(self.discovery:set_endpoint_identity('012B0242-2022-A6E3-11EE-006322669F60')) | ||
| 138 | + self:request(3) | ||
| 139 | + local expected = '\xe0\x10\xd7\x9d\x75\xda\x5d\x5b\x9a\xfe\xf5\x6b\x26\x2f\x8c\x36' | ||
| 140 | + lu.assertEquals(self.frames[#self.frames]:sub(20, 36), '\0' .. expected) | ||
| 141 | + lu.assertFalse(self.discovery:set_endpoint_identity('00000000-0000-0000-0000-000000000000')) | ||
| 142 | + self:request(1, '\0\12', 9) | ||
| 143 | + self.engine:set_bmc_eid(11, 0x300) | ||
| 144 | + self:request(3) | ||
| 145 | + lu.assertEquals(self.frames[#self.frames]:sub(21, 36), expected) | ||
| 146 | + lu.assertTrue(self.discovery:set_endpoint_identity('012b02422022a6e311ee006322669f60')) | ||
| 147 | + lu.assertEquals(self.discovery.endpoint_uuid, expected) | ||
| 148 | +end | ||
| 149 | + | ||
| 150 | +function TestMctpControlAdmission:test_version_type_and_vendor_queries_reflect_responder_registration() | ||
| 151 | + -- DSP0236 1.3.1勘误:Control与Base版本列表不同,保留兼容版本。 | ||
| 152 | + self:request(4, '\0') | ||
| 153 | + lu.assertEquals(self.frames[#self.frames]:sub(20, 33), '\0\3\xf1\xf0\xff\0\xf1\xf1\xff\0\xf1\xf2\xff\0') | ||
| 154 | + self:request(4, '\xff') | ||
| 155 | + lu.assertEquals( | ||
| 156 | + self.frames[#self.frames]:sub(20, 37), | ||
| 157 | + '\0\4\xf1\xf0\xff\0\xf1\xf1\xff\0\xf1\xf2\xff\0\xf1\xf3\xf1\0' | ||
| 158 | + ) | ||
| 159 | + self:request(5) | ||
| 160 | + lu.assertEquals(self.frames[#self.frames]:sub(20, 21), '\0\0') | ||
| 161 | + self:request(4, '\1') | ||
| 162 | + lu.assertEquals(self.frames[#self.frames]:byte(20), 0x80) | ||
| 163 | + self.engine.pldm_responder = { reg_count = 1 } | ||
| 164 | + self:request(5) | ||
| 165 | + lu.assertEquals(self.frames[#self.frames]:sub(20, 22), '\0\1\1') | ||
| 166 | + self:request(4, '\1') | ||
| 167 | + lu.assertEquals(self.frames[#self.frames]:sub(20, 25), '\0\1\xf1\xf0\xf0\0') | ||
| 168 | + self.engine.pldm_responder.reg_count = 0 | ||
| 169 | + self:request(5) | ||
| 170 | + lu.assertEquals(self.frames[#self.frames]:sub(20, 21), '\0\0') | ||
| 171 | + for _, registry in ipairs({ {}, { reg_count = '1' } }) do | ||
| 172 | + self.engine.pldm_responder = registry | ||
| 173 | + lu.assertFalse(self.discovery:has_pldm_responder()) | ||
| 174 | + end | ||
| 175 | + for _, selector in ipairs({ 0, 1, 255 }) do | ||
| 176 | + self:request(6, string.char(selector)) | ||
| 177 | + lu.assertEquals(self.frames[#self.frames]:byte(20), 5) | ||
| 178 | + end | ||
| 179 | +end | ||
| 180 | + | ||
| 181 | +function TestMctpControlAdmission:test_identity_background_retries_and_stops_after_success() | ||
| 182 | + local previous, tasks = c_tasks.get_instance, {} | ||
| 183 | + c_tasks.get_instance = function() | ||
| 184 | + return { | ||
| 185 | + new_task = function(_, name) | ||
| 186 | + local task = make_fake_task() | ||
| 187 | + function task:loop(cb) | ||
| 188 | + self.callback = cb | ||
| 189 | + return self | ||
| 190 | + end | ||
| 191 | + function task:set_timeout_ms(ms) | ||
| 192 | + self.timeout_ms = ms | ||
| 193 | + return self | ||
| 194 | + end | ||
| 195 | + function task:stop() | ||
| 196 | + self.stopped = true | ||
| 197 | + end | ||
| 198 | + tasks[name] = task | ||
| 199 | + return task | ||
| 200 | + end, | ||
| 201 | + } | ||
| 202 | + end | ||
| 203 | + self.engine.bus = {} | ||
| 204 | + local ok, obj = pcall(c_mctp_discovery.new, 7, self.engine) | ||
| 205 | + c_tasks.get_instance = previous | ||
| 206 | + lu.assertTrue(ok, tostring(obj)) | ||
| 207 | + local task = tasks.mctp_identity7 | ||
| 208 | + lu.assertNotNil(task) | ||
| 209 | + lu.assertEquals(task.timeout_ms, 1000) | ||
| 210 | + local calls = 0 | ||
| 211 | + obj.load_endpoint_identity = function(self) | ||
| 212 | + calls = calls + 1 | ||
| 213 | + return calls > 1 and self:set_endpoint_identity('012B0242-2022-A6E3-11EE-006322669F60') | ||
| 214 | + end | ||
| 215 | + task.callback(task) | ||
| 216 | + lu.assertFalse(task.stopped == true) | ||
| 217 | + task.callback(task) | ||
| 218 | + lu.assertNotNil(obj.endpoint_uuid) | ||
| 219 | + lu.assertTrue(task.stopped) | ||
| 220 | + lu.assertEquals(calls, 2) | ||
| 221 | +end | ||
| 222 | + | ||
| 223 | +function TestMctpControlAdmission:test_identity_discovery_retries_and_freezes_valid_device_uuid() | ||
| 224 | + local mode, calls = 'missing', {} | ||
| 225 | + self.engine.bus = { | ||
| 226 | + timeout_call = function(_, budget, service, path, interface, method) | ||
| 227 | + calls[#calls + 1] = method | ||
| 228 | + lu.assertEquals( | ||
| 229 | + { budget, service, path, interface, method }, | ||
| 230 | + { 100, 'bmc.kepler.maca', '/bmc/kepler/MdbService', 'bmc.kepler.Mdb', 'GetSubObjects' } | ||
| 231 | + ) | ||
| 232 | + if mode == 'error' then | ||
| 233 | + error('unavailable') | ||
| 234 | + end | ||
| 235 | + if mode == 'missing' then | ||
| 236 | + return {} | ||
| 237 | + end | ||
| 238 | + if mode == 'ambiguous' then | ||
| 239 | + return { ['/a'] = {}, ['/b'] = {} } | ||
| 240 | + end | ||
| 241 | + return { ['/bmc/kepler/Managers/1/Assembly'] = { ['bmc.kepler.bmc_network'] = {} } } | ||
| 242 | + end, | ||
| 243 | + bus = { | ||
| 244 | + timeout_call = function(_, budget, service, path, interface, method) | ||
| 245 | + calls[#calls + 1] = method | ||
| 246 | + lu.assertEquals({ budget, service, path, interface, method }, { | ||
| 247 | + 100, | ||
| 248 | + 'bmc.kepler.bmc_network', | ||
| 249 | + '/bmc/kepler/Managers/1/Assembly', | ||
| 250 | + 'bmc.kepler.Object.Properties', | ||
| 251 | + 'GetAllWithContext', | ||
| 252 | + }) | ||
| 253 | + return { | ||
| 254 | + read = function() | ||
| 255 | + return { | ||
| 256 | + UUID = { | ||
| 257 | + value = function() | ||
| 258 | + return mode == 'invalid' and 'invalid' or '012B0242-2022-A6E3-11EE-006322669F60' | ||
| 259 | + end, | ||
| 260 | + }, | ||
| 261 | + } | ||
| 262 | + end, | ||
| 263 | + } | ||
| 264 | + end, | ||
| 265 | + }, | ||
| 266 | + } | ||
| 267 | + for _, state in ipairs({ 'missing', 'error', 'ambiguous', 'invalid' }) do | ||
| 268 | + mode = state | ||
| 269 | + lu.assertFalse(self.discovery:load_endpoint_identity()) | ||
| 270 | + lu.assertIsNil(self.discovery.endpoint_uuid) | ||
| 271 | + end | ||
| 272 | + mode = 'valid' | ||
| 273 | + lu.assertTrue(self.discovery:load_endpoint_identity()) | ||
| 274 | + local uuid, completed_calls = self.discovery.endpoint_uuid, #calls | ||
| 275 | + mode = 'error' | ||
| 276 | + lu.assertTrue(self.discovery:load_endpoint_identity()) | ||
| 277 | + lu.assertEquals(#calls, completed_calls) | ||
| 278 | + lu.assertFalse(self.discovery:set_endpoint_identity('112B0242-2022-A6E3-11EE-006322669F60')) | ||
| 279 | + lu.assertEquals(self.discovery.endpoint_uuid, uuid) | ||
| 280 | +end | ||
🔵 Low Priority 新增的核心行为"后台有界读取掉电保存的 Assembly.UUID"依赖 证据链:新增测试 失败模式:若 建议:补一个用例:用带 ![]() ![]() Incorrect? | |||
| 281 | + | ||
| 36 | TestMctpDiscovery = {} | 282 | TestMctpDiscovery = {} |
| 37 | 283 | ||
| 38 | function TestMctpDiscovery:setupClass() | 284 | function TestMctpDiscovery:setupClass() |
| @@ -114,6 +360,7 @@ function TestMctpDiscovery:test_mctp_discovery_success() | |||
| 114 | end) | 360 | end) |
| 115 | 361 | ||
| 116 | self.mctp_discovery:smbus_set_eid_to_endpoint( | 362 | self.mctp_discovery:smbus_set_eid_to_endpoint( |
| 363 | + 'discovery-success', | ||
| 117 | target_eid, | 364 | target_eid, |
| 118 | ref_chip, | 365 | ref_chip, |
| 119 | medium_type, | 366 | medium_type, |
| @@ -158,6 +405,7 @@ function TestMctpDiscovery:test_mctp_discovery_failed() | |||
| 158 | local ref_chip = { path = path } | 405 | local ref_chip = { path = path } |
| 159 | 406 | ||
| 160 | self.mctp_discovery:smbus_set_eid_to_endpoint( | 407 | self.mctp_discovery:smbus_set_eid_to_endpoint( |
| 408 | + 'discovery-failed', | ||
| 161 | target_eid, | 409 | target_eid, |
| 162 | ref_chip, | 410 | ref_chip, |
| 163 | medium_type, | 411 | medium_type, |


🔵 Low Priority
新增的 init 分支(mctp_discovery.lua:82-92)用
c_tasks.get_instance():new_task('mctp_identity'..system_id):loop(...)注册每 1000ms 执行一次的后台任务,但循环体只有if not self.endpoint_uuid then self:load_endpoint_identity() end,成功后没有任何:stop()/返回终止逻辑,self.identity_task也从未在其它地方被引用(全仓仅此一处赋值)。证据链:同类任务
start_update_route_table/start_smbus_eid_poll_task均有显式 stop 入口(stop_update_route_table等),而身份任务没有;且update_os_power_status(false)→set_discovered(false)→on_discovery_changed只做mctp_engine:reset()与stop_update_route_table(),不会停止身份任务。失败模式:身份一旦加载成功,该任务仍按 1Hz 永久唤醒(每个 system 一个),空转消耗 skynet 定时器与调度;系统下电/复位也不会停止,属于长期存在的后台空转任务。功能上不致命,但属于本次新增的可靠性/资源浪费点。
建议:在循环体内于身份加载成功后停止任务,例如
if self.endpoint_uuid then self.identity_task:stop(); return end,并在update_os_power_status/mctp_engine:reset()路径中一并停止或重启该任务,避免永久空转。