1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 * Copyright (c) 2018-present Facebook.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <biccommands.hpp>
19 #include <commandutils.hpp>
20 #include <ipmid/api-types.hpp>
21 #include <ipmid/api.hpp>
22 #include <phosphor-logging/log.hpp>
23 #include <types.hpp>
24
25 #include <iostream>
26 #include <variant>
27 #include <vector>
28
29 namespace ipmi
30 {
31
32 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&,
33 std::vector<uint8_t>&);
34
35 using namespace phosphor::logging;
36
37 #ifdef BIC_ENABLED
38 static void registerBICFunctions() __attribute__((constructor));
39 #endif
40
41 extern message::Response::ptr executeIpmiCommand(message::Request::ptr);
42
43 int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&,
44 std::vector<uint8_t>&);
45
46 constexpr std::array<uint8_t, 2> amdDimmLoopPrefix = {0xDD, 0xEE};
47
48 namespace dimm
49 {
50 std::unordered_map<hostId, dimmLoop> dimmLoops;
51 } // namespace dimm
52
53 //----------------------------------------------------------------------
54 // ipmiOemBicHandler (IPMI/Section - ) (CMD_OEM_BIC_INFO)
55 // This Function will handle BIC request for netfn=0x38 and cmd=1
56 // send the response back to the sender.
57 //----------------------------------------------------------------------
58
59 ipmi::RspType<IanaType, uint8_t, uint2_t, uint6_t, uint8_t, uint8_t,
60 ipmi::message::Payload>
ipmiOemBicHandler(ipmi::Context::ptr ctx,IanaType reqIana,uint8_t interface,uint2_t lun,uint6_t netFnReq,uint8_t cmdReq,SecureBuffer data)61 ipmiOemBicHandler(ipmi::Context::ptr ctx, IanaType reqIana,
62 uint8_t interface, uint2_t lun, uint6_t netFnReq,
63 uint8_t cmdReq, SecureBuffer data)
64 {
65 ipmi::message::Response::ptr res;
66
67 // Updating the correct netfn and cmd in the ipmi Context
68 ctx->netFn = ((uint8_t)netFnReq);
69 ctx->cmd = cmdReq;
70
71 // creating ipmi message request for calling executeIpmiCommand function
72 auto req = std::make_shared<ipmi::message::Request>(ctx, std::move(data));
73
74 // Calling executeIpmiCommand request function
75 res = ipmi::executeIpmiCommand(req);
76
77 // sending the response with headers and payload
78 return ipmi::responseSuccess(reqIana, interface, lun, ++netFnReq, cmdReq,
79 res->cc, res->payload);
80 }
81
dimmLoopPatternDetection(size_t hostId,std::vector<uint8_t> data)82 void dimmLoopPatternDetection(size_t hostId, std::vector<uint8_t> data)
83 {
84 if constexpr (postCodeSize != amdFourBytesPostCode)
85 {
86 return;
87 }
88
89 if (data.size() != amdFourBytesPostCode)
90 {
91 return;
92 }
93
94 /*
95 Reference from Meta_BIOS_Requirement_Spec_v0.80
96 For AMD platform, the POST code looping pattern format should be:
97 (each group has 4 bytes)
98 ●Group #0: [DDEE0000]
99 ●Group #1: [DDEE] + Total Error Count
100 ●Group #2: [DDEE] + Number of Error DIMM
101 ●Group #3: [DDEE] + Dimm location
102 ●Group #4: [DDEE] + major code
103 ●Group #5: [DDEE] + minor code
104 */
105 std::array<uint8_t, 2> prefix = {data[3], data[2]};
106
107 if (prefix != amdDimmLoopPrefix)
108 {
109 // Clear all the post code stored before.
110 if (dimm::dimmLoops[hostId].startDetect)
111 {
112 dimm::dimmLoops[hostId].totalErrorCount = 0;
113 dimm::dimmLoops[hostId].postCode.clear();
114
115 dimm::dimmLoops[hostId].startDetect = false;
116 }
117 return;
118 }
119
120 // Which means it already got the dimm loop, stop checking again.
121 if (dimm::dimmLoops[hostId].gotPattern)
122 {
123 return;
124 }
125
126 constexpr std::array<uint8_t, 4> anchorTag = {0x0, 0x0, 0xEE, 0xDD};
127 if (std::ranges::equal(anchorTag, data))
128 {
129 dimm::dimmLoops[hostId].startDetect = true;
130 }
131 if (dimm::dimmLoops[hostId].startDetect)
132 {
133 // The second one is error count
134 if (dimm::dimmLoops[hostId].postCode.size() % 6 == 1)
135 {
136 dimm::dimmLoops[hostId].totalErrorCount = (data[1] << 8) | data[0];
137 }
138
139 dimm::dimmLoops[hostId].postCode.push_back(data);
140
141 // Is the last element of dimmloop then stop to detect
142 if (dimm::dimmLoops[hostId].postCode.size() ==
143 (dimm::dimmLoops[hostId].totalErrorCount * 6))
144 {
145 // Gets whole pattern success
146 dimm::dimmLoops[hostId].gotPattern = true;
147 }
148 }
149 }
150
151 //----------------------------------------------------------------------
152 // ipmiOemPostCodeHandler (CMD_OEM_BIC_POST_BUFFER_INFO)
153 // This Function will handle BIC incoming postcode from multi-host for
154 // netfn=0x38 and cmd=0x08 or 0x33 send the response back to the sender.
155 //----------------------------------------------------------------------
156
ipmiOemPostCodeHandler(ipmi::Context::ptr ctx,IanaType reqIana,uint8_t dataLen,std::vector<uint8_t> data)157 ipmi::RspType<IanaType> ipmiOemPostCodeHandler(
158 ipmi::Context::ptr ctx, IanaType reqIana, uint8_t dataLen,
159 std::vector<uint8_t> data)
160 {
161 // creating bus connection
162 auto conn = getSdBus();
163
164 auto hostId = findHost(ctx->hostIdx);
165 if (!hostId)
166 {
167 lg2::error("Invalid Host Id received");
168 return ipmi::responseInvalidCommand();
169 }
170 dimmLoopPatternDetection(*hostId, data);
171
172 using postcode_t = std::tuple<uint64_t, std::vector<uint8_t>>;
173
174 std::string dbusObjStr = dbusObj + std::to_string((ctx->hostIdx + 1));
175
176 for (unsigned int index = 0; index < dataLen; index++)
177 {
178 uint64_t primaryPostCode = static_cast<uint64_t>(data[index]);
179 auto postCode = postcode_t(primaryPostCode, {});
180
181 try
182 {
183 auto method = conn->new_method_call(
184 "xyz.openbmc_project.State.Boot.Raw", dbusObjStr.c_str(),
185 "org.freedesktop.DBus.Properties", "Set");
186
187 // Adding parameters to method call
188 method.append(dbusService, "Value",
189 std::variant<postcode_t>(postCode));
190
191 // Invoke method call function
192 auto reply = conn->call(method);
193 }
194
195 catch (std::exception& e)
196 {
197 phosphor::logging::log<phosphor::logging::level::ERR>(
198 "post code handler error\n");
199
200 // sending the Error response
201 return ipmi::responseResponseError();
202 }
203 }
204
205 return ipmi::responseSuccess(reqIana);
206 }
207
208 //----------------------------------------------------------------------
209 // ipmiOemGetBicGpioState (CMD_OEM_GET_BIC_GPIO_STATE)
210 // This Function will handle BIC GPIO stats for
211 // netfn=0x38 and cmd=0x03 send the response back to the sender.
212 //----------------------------------------------------------------------
213
ipmiOemGetBicGpioState(ipmi::Context::ptr ctx,std::vector<uint8_t> reqIana)214 ipmi::RspType<IanaType, std::vector<uint8_t>> ipmiOemGetBicGpioState(
215 ipmi::Context::ptr ctx, std::vector<uint8_t> reqIana)
216 {
217 std::vector<uint8_t> respData;
218
219 if (std::equal(reqIana.begin(), reqIana.end(), iana.begin()) == false)
220 {
221 phosphor::logging::log<phosphor::logging::level::ERR>(
222 "Invalid IANA number");
223 return ipmi::responseInvalidFieldRequest();
224 }
225
226 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
227
228 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqIana, respData))
229 {
230 return ipmi::responseUnspecifiedError();
231 }
232
233 std::vector<uint8_t> gpioState;
234 IanaType respIana;
235
236 auto r =
237 std::ranges::copy_n(respData.begin(), iana.size(), respIana.begin()).in;
238 std::copy(r, respData.end(), std::back_inserter(gpioState));
239
240 return ipmi::responseSuccess(respIana, gpioState);
241 }
242
243 //----------------------------------------------------------------------
244 // ipmiOemSetHostPowerState (CMD_OEM_SET_HOST_POWER_STATE)
245 // This Function will handle BIC incoming IPMI request for
246 // setting host current state for netfn=0x38 and cmd=0x0C
247 // send the response back to the sender.
248 //----------------------------------------------------------------------
249
ipmiOemSetHostPowerState(ipmi::Context::ptr ctx,IanaType reqIana,uint8_t status)250 ipmi::RspType<IanaType> ipmiOemSetHostPowerState(
251 ipmi::Context::ptr ctx, IanaType reqIana, uint8_t status)
252 {
253 std::string targetUnit;
254
255 switch (static_cast<HostPowerState>(status))
256 {
257 case HostPowerState::HOST_POWER_ON:
258 targetUnit = "obmc-host-startmin@.target";
259 break;
260 case HostPowerState::HOST_POWER_OFF:
261 targetUnit = "obmc-host-stop@.target";
262 break;
263 default:
264 phosphor::logging::log<phosphor::logging::level::ERR>(
265 "IPMI ipmiOemHostPowerStatus power status error");
266 return ipmi::responseUnspecifiedError();
267 }
268
269 int mousePos = targetUnit.find('@');
270 targetUnit.insert(mousePos + 1, std::to_string(ctx->hostIdx + 1));
271
272 auto conn = getSdBus();
273 auto method = conn->new_method_call(systemdService, systemdObjPath,
274 systemdInterface, "StartUnit");
275 method.append(targetUnit);
276 method.append("replace");
277
278 try
279 {
280 conn->call_noreply(method);
281 }
282 catch (const sdbusplus::exception::SdBusError& e)
283 {
284 phosphor::logging::log<phosphor::logging::level::ERR>(
285 "IPMI ipmiOemHostPowerStatus Failed in call method",
286 phosphor::logging::entry("ERROR=%s", e.what()));
287 return ipmi::responseUnspecifiedError();
288 }
289
290 return ipmi::responseSuccess(reqIana);
291 }
292
293 //----------------------------------------------------------------------
294 // ipmiOemGetBiosFlashSize (CMD_OEM_GET_FLASH_SIZE)
295 // This Function will return the bios flash size
296 // netfn=0x38 and cmd=0x19 send the response back to the sender.
297 //----------------------------------------------------------------------
298
ipmiOemGetBiosFlashSize(ipmi::Context::ptr ctx,IanaType ianaReq,uint8_t target)299 ipmi::RspType<IanaType, flashSize> ipmiOemGetBiosFlashSize(
300 ipmi::Context::ptr ctx, IanaType ianaReq, uint8_t target)
301 {
302 if (iana != ianaReq)
303 {
304 phosphor::logging::log<phosphor::logging::level::ERR>(
305 "Invalid IANA ID length received");
306 return ipmi::responseReqDataLenInvalid();
307 }
308
309 std::vector<uint8_t> respData;
310 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
311 std::vector<uint8_t> reqData(ianaReq.begin(), ianaReq.end());
312 reqData.emplace_back(target);
313
314 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData))
315 {
316 return ipmi::responseUnspecifiedError();
317 }
318
319 if (respData.size() != flashSizeRespLen)
320 {
321 phosphor::logging::log<phosphor::logging::level::ERR>(
322 "Invalid Response Data length received");
323 return ipmi::responseReqDataLenInvalid();
324 }
325
326 IanaType ianaResp;
327 std::copy_n(respData.begin(), ianaResp.size(), ianaResp.begin());
328
329 if (iana != ianaResp)
330 {
331 phosphor::logging::log<phosphor::logging::level::ERR>(
332 "Invalid IANA ID received");
333 return ipmi::responseInvalidCommand();
334 }
335
336 flashSize flashResp;
337 std::vector<uint8_t>::iterator respDataIter = respData.begin();
338 std::advance(respDataIter, ianaResp.size());
339 std::copy_n(respDataIter, flashResp.size(), flashResp.begin());
340
341 // sending the success response.
342 return ipmi::responseSuccess(ianaResp, flashResp);
343 }
344
345 //----------------------------------------------------------------------
346 // ipmiOemClearCmos (CMD_OEM_CLEAR_CMOS)
347 // This Function will clear the CMOS.
348 // netfn=0x38 and cmd=0x25
349 //----------------------------------------------------------------------
ipmiOemClearCmos(ipmi::Context::ptr ctx,IanaType ianaReq)350 ipmi::RspType<IanaType> ipmiOemClearCmos(ipmi::Context::ptr ctx,
351 IanaType ianaReq)
352 {
353 if (iana != ianaReq)
354 {
355 phosphor::logging::log<phosphor::logging::level::ERR>(
356 "Invalid request of IANA ID length received");
357 return ipmi::responseReqDataLenInvalid();
358 }
359
360 uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
361
362 std::vector<uint8_t> respData;
363 std::vector<uint8_t> reqData(ianaReq.begin(), ianaReq.end());
364
365 if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData))
366 {
367 return ipmi::responseUnspecifiedError();
368 }
369
370 if (respData.size() != iana.size())
371 {
372 return ipmi::responseReqDataLenInvalid();
373 }
374
375 IanaType resp;
376 std::copy_n(respData.begin(), resp.size(), resp.begin());
377
378 if (iana != resp)
379 {
380 phosphor::logging::log<phosphor::logging::level::ERR>(
381 "Invalid response of IANA ID received");
382 return ipmi::responseUnspecifiedError();
383 }
384
385 // sending the success response.
386 return ipmi::responseSuccess(resp);
387 }
388
registerBICFunctions(void)389 [[maybe_unused]] static void registerBICFunctions(void)
390 {
391 phosphor::logging::log<phosphor::logging::level::INFO>(
392 "Registering BIC commands");
393
394 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
395 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_BIC_INFO),
396 ipmi::Privilege::User, ipmiOemBicHandler);
397 ipmi::registerHandler(
398 ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
399 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SEND_POST_BUFFER_TO_BMC),
400 ipmi::Privilege::User, ipmiOemPostCodeHandler);
401 ipmi::registerHandler(
402 ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
403 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_1S_4BYTE_POST_BUF),
404 ipmi::Privilege::User, ipmiOemPostCodeHandler);
405 ipmi::registerHandler(
406 ipmi::prioOemBase, ipmi::netFnOemFive,
407 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_BIC_GPIO_STATE),
408 ipmi::Privilege::User, ipmiOemGetBicGpioState);
409 ipmi::registerHandler(
410 ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
411 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SET_HOST_POWER_STATE),
412 ipmi::Privilege::User, ipmiOemSetHostPowerState);
413 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
414 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_FLASH_SIZE),
415 ipmi::Privilege::User, ipmiOemGetBiosFlashSize);
416 ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
417 static_cast<Cmd>(fb_bic_cmds::CMD_OEM_CLEAR_CMOS),
418 ipmi::Privilege::User, ipmiOemClearCmos);
419 return;
420 }
421
422 } // namespace ipmi
423