xref: /openbmc/fb-ipmi-oem/src/biccommands.cpp (revision b647d63f)
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>
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 
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 incomming postcode from multi-host for
154 // netfn=0x38 and cmd=0x08 or 0x33 send the response back to the sender.
155 //----------------------------------------------------------------------
156 
157 ipmi::RspType<IanaType> ipmiOemPostCodeHandler(ipmi::Context::ptr ctx,
158                                                IanaType reqIana,
159                                                uint8_t dataLen,
160                                                std::vector<uint8_t> data)
161 {
162     // creating bus connection
163     auto conn = getSdBus();
164 
165     auto hostId = findHost(ctx->hostIdx);
166     if (!hostId)
167     {
168         lg2::error("Invalid Host Id received");
169         return ipmi::responseInvalidCommand();
170     }
171     dimmLoopPatternDetection(*hostId, data);
172 
173     using postcode_t = std::tuple<uint64_t, std::vector<uint8_t>>;
174 
175     std::string dbusObjStr = dbusObj + std::to_string((ctx->hostIdx + 1));
176 
177     for (unsigned int index = 0; index < dataLen; index++)
178     {
179         uint64_t primaryPostCode = static_cast<uint64_t>(data[index]);
180         auto postCode = postcode_t(primaryPostCode, {});
181 
182         try
183         {
184             auto method = conn->new_method_call(
185                 "xyz.openbmc_project.State.Boot.Raw", dbusObjStr.c_str(),
186                 "org.freedesktop.DBus.Properties", "Set");
187 
188             // Adding paramters to method call
189             method.append(dbusService, "Value",
190                           std::variant<postcode_t>(postCode));
191 
192             // Invoke method call function
193             auto reply = conn->call(method);
194         }
195 
196         catch (std::exception& e)
197         {
198             phosphor::logging::log<phosphor::logging::level::ERR>(
199                 "post code handler error\n");
200 
201             // sending the Error response
202             return ipmi::responseResponseError();
203         }
204     }
205 
206     return ipmi::responseSuccess(reqIana);
207 }
208 
209 //----------------------------------------------------------------------
210 // ipmiOemGetBicGpioState (CMD_OEM_GET_BIC_GPIO_STATE)
211 // This Function will handle BIC GPIO stats for
212 // netfn=0x38 and cmd=0x03 send the response back to the sender.
213 //----------------------------------------------------------------------
214 
215 ipmi::RspType<IanaType, std::vector<uint8_t>>
216     ipmiOemGetBicGpioState(ipmi::Context::ptr ctx, std::vector<uint8_t> reqIana)
217 {
218     std::vector<uint8_t> respData;
219 
220     if (std::equal(reqIana.begin(), reqIana.end(), iana.begin()) == false)
221     {
222         phosphor::logging::log<phosphor::logging::level::ERR>(
223             "Invalid IANA number");
224         return ipmi::responseInvalidFieldRequest();
225     }
226 
227     uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
228 
229     if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqIana, respData))
230     {
231         return ipmi::responseUnspecifiedError();
232     }
233 
234     std::vector<uint8_t> gpioState;
235     IanaType respIana;
236 
237     auto r =
238         std::ranges::copy_n(respData.begin(), iana.size(), respIana.begin()).in;
239     std::copy(r, respData.end(), std::back_inserter(gpioState));
240 
241     return ipmi::responseSuccess(respIana, gpioState);
242 }
243 
244 //----------------------------------------------------------------------
245 // ipmiOemSetHostPowerState (CMD_OEM_SET_HOST_POWER_STATE)
246 // This Function will handle BIC incomming IPMI request for
247 // setting host current state for netfn=0x38 and cmd=0x0C
248 // send the response back to the sender.
249 //----------------------------------------------------------------------
250 
251 ipmi::RspType<IanaType> ipmiOemSetHostPowerState(ipmi::Context::ptr ctx,
252                                                  IanaType reqIana,
253                                                  uint8_t status)
254 {
255     std::string targetUnit;
256 
257     switch (static_cast<HostPowerState>(status))
258     {
259         case HostPowerState::HOST_POWER_ON:
260             targetUnit = "obmc-host-startmin@.target";
261             break;
262         case HostPowerState::HOST_POWER_OFF:
263             targetUnit = "obmc-host-stop@.target";
264             break;
265         default:
266             phosphor::logging::log<phosphor::logging::level::ERR>(
267                 "IPMI ipmiOemHostPowerStatus power status error");
268             return ipmi::responseUnspecifiedError();
269     }
270 
271     int mousePos = targetUnit.find('@');
272     targetUnit.insert(mousePos + 1, std::to_string(ctx->hostIdx + 1));
273 
274     auto conn = getSdBus();
275     auto method = conn->new_method_call(systemdService, systemdObjPath,
276                                         systemdInterface, "StartUnit");
277     method.append(targetUnit);
278     method.append("replace");
279 
280     try
281     {
282         conn->call_noreply(method);
283     }
284     catch (const sdbusplus::exception::SdBusError& e)
285     {
286         phosphor::logging::log<phosphor::logging::level::ERR>(
287             "IPMI ipmiOemHostPowerStatus Failed in call method",
288             phosphor::logging::entry("ERROR=%s", e.what()));
289         return ipmi::responseUnspecifiedError();
290     }
291 
292     return ipmi::responseSuccess(reqIana);
293 }
294 
295 //----------------------------------------------------------------------
296 // ipmiOemGetBiosFlashSize (CMD_OEM_GET_FLASH_SIZE)
297 // This Function will return the bios flash size
298 // netfn=0x38 and cmd=0x19 send the response back to the sender.
299 //----------------------------------------------------------------------
300 
301 ipmi::RspType<IanaType, flashSize>
302     ipmiOemGetBiosFlashSize(ipmi::Context::ptr ctx, IanaType ianaReq,
303                             uint8_t target)
304 {
305     if (iana != ianaReq)
306     {
307         phosphor::logging::log<phosphor::logging::level::ERR>(
308             "Invalid IANA ID length received");
309         return ipmi::responseReqDataLenInvalid();
310     }
311 
312     std::vector<uint8_t> respData;
313     uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
314     std::vector<uint8_t> reqData(ianaReq.begin(), ianaReq.end());
315     reqData.emplace_back(target);
316 
317     if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData))
318     {
319         return ipmi::responseUnspecifiedError();
320     }
321 
322     if (respData.size() != flashSizeRespLen)
323     {
324         phosphor::logging::log<phosphor::logging::level::ERR>(
325             "Invalid Response Data length received");
326         return ipmi::responseReqDataLenInvalid();
327     }
328 
329     IanaType ianaResp;
330     std::copy_n(respData.begin(), ianaResp.size(), ianaResp.begin());
331 
332     if (iana != ianaResp)
333     {
334         phosphor::logging::log<phosphor::logging::level::ERR>(
335             "Invalid IANA ID received");
336         return ipmi::responseInvalidCommand();
337     }
338 
339     flashSize flashResp;
340     std::vector<uint8_t>::iterator respDataIter = respData.begin();
341     std::advance(respDataIter, ianaResp.size());
342     std::copy_n(respDataIter, flashResp.size(), flashResp.begin());
343 
344     // sending the success response.
345     return ipmi::responseSuccess(ianaResp, flashResp);
346 }
347 
348 //----------------------------------------------------------------------
349 // ipmiOemClearCmos (CMD_OEM_CLEAR_CMOS)
350 // This Function will clear the CMOS.
351 // netfn=0x38 and cmd=0x25
352 //----------------------------------------------------------------------
353 ipmi::RspType<IanaType> ipmiOemClearCmos(ipmi::Context::ptr ctx,
354                                          IanaType ianaReq)
355 {
356     if (iana != ianaReq)
357     {
358         phosphor::logging::log<phosphor::logging::level::ERR>(
359             "Invalid request of IANA ID length received");
360         return ipmi::responseReqDataLenInvalid();
361     }
362 
363     uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
364 
365     std::vector<uint8_t> respData;
366     std::vector<uint8_t> reqData(ianaReq.begin(), ianaReq.end());
367 
368     if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData))
369     {
370         return ipmi::responseUnspecifiedError();
371     }
372 
373     if (respData.size() != iana.size())
374     {
375         return ipmi::responseReqDataLenInvalid();
376     }
377 
378     IanaType resp;
379     std::copy_n(respData.begin(), resp.size(), resp.begin());
380 
381     if (iana != resp)
382     {
383         phosphor::logging::log<phosphor::logging::level::ERR>(
384             "Invalid response of IANA ID received");
385         return ipmi::responseUnspecifiedError();
386     }
387 
388     // sending the success response.
389     return ipmi::responseSuccess(resp);
390 }
391 
392 [[maybe_unused]] static void registerBICFunctions(void)
393 {
394     phosphor::logging::log<phosphor::logging::level::INFO>(
395         "Registering BIC commands");
396 
397     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
398                           static_cast<Cmd>(fb_bic_cmds::CMD_OEM_BIC_INFO),
399                           ipmi::Privilege::User, ipmiOemBicHandler);
400     ipmi::registerHandler(
401         ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
402         static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SEND_POST_BUFFER_TO_BMC),
403         ipmi::Privilege::User, ipmiOemPostCodeHandler);
404     ipmi::registerHandler(
405         ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
406         static_cast<Cmd>(fb_bic_cmds::CMD_OEM_1S_4BYTE_POST_BUF),
407         ipmi::Privilege::User, ipmiOemPostCodeHandler);
408     ipmi::registerHandler(
409         ipmi::prioOemBase, ipmi::netFnOemFive,
410         static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_BIC_GPIO_STATE),
411         ipmi::Privilege::User, ipmiOemGetBicGpioState);
412     ipmi::registerHandler(
413         ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
414         static_cast<Cmd>(fb_bic_cmds::CMD_OEM_SET_HOST_POWER_STATE),
415         ipmi::Privilege::User, ipmiOemSetHostPowerState);
416     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
417                           static_cast<Cmd>(fb_bic_cmds::CMD_OEM_GET_FLASH_SIZE),
418                           ipmi::Privilege::User, ipmiOemGetBiosFlashSize);
419     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnOemFive,
420                           static_cast<Cmd>(fb_bic_cmds::CMD_OEM_CLEAR_CMOS),
421                           ipmi::Privilege::User, ipmiOemClearCmos);
422     return;
423 }
424 
425 } // namespace ipmi
426