xref: /openbmc/fb-ipmi-oem/src/biccommands.cpp (revision 1caf04862ed2a8c65f441f5762ec762bf912248e)
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