xref: /openbmc/fb-ipmi-oem/src/biccommands.cpp (revision 010dee04)
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 
157 ipmi::RspType<IanaType>
ipmiOemPostCodeHandler(ipmi::Context::ptr ctx,IanaType reqIana,uint8_t dataLen,std::vector<uint8_t> data)158     ipmiOemPostCodeHandler(ipmi::Context::ptr ctx, IanaType reqIana,
159                            uint8_t dataLen, 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 
214 ipmi::RspType<IanaType, std::vector<uint8_t>>
ipmiOemGetBicGpioState(ipmi::Context::ptr ctx,std::vector<uint8_t> reqIana)215     ipmiOemGetBicGpioState(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