xref: /openbmc/phosphor-host-ipmid/sbmrhandler.cpp (revision 0a3f40b92c44bb196e355f845c6aafd1d2dce5fe)
1 #include <ipmid/api.hpp>
2 #include <ipmid/filter.hpp>
3 #include <ipmid/utils.hpp>
4 #include <phosphor-logging/lg2.hpp>
5 
6 constexpr auto sbmrBootStateIntf = "xyz.openbmc_project.State.Boot.Raw";
7 constexpr auto sbmrHostStateIntf = "xyz.openbmc_project.State.Boot.Progress";
8 constexpr auto sbmrBootProgressCodeSize = 9;
9 
10 constexpr auto bootProgressOem = "OEM";
11 constexpr auto bootProgressOsRuning = "OSRunning";
12 constexpr auto bootProgressOsStart = "OSStart";
13 constexpr auto bootProgressPciInit = "PCIInit";
14 constexpr auto bootProgressSystemInitComplete = "SystemInitComplete";
15 constexpr auto bootProgressSystemSetup = "SystemSetup";
16 
17 // EFI_STATUS_CODE_TYPE
18 constexpr auto efiProgressCode = 0x01;
19 constexpr auto efiCodeSeverityNone = 0;
20 
21 // EFI_STATUS_CODE_CLASS
22 constexpr auto efiIoBus = 0x02;
23 constexpr auto efiSoftware = 0x03;
24 
25 // EFI_STATUS_CODE_SUBCLASS
26 constexpr auto efiIoBusPci = 0x01;
27 constexpr auto efiSoftwareDxeCore = 0x04;
28 constexpr auto efiSoftwareDxeBsDriver = 0x05;
29 constexpr auto efiSoftwareEfiBootService = 0x10;
30 
31 // EFI_STATUS_CODE_OPERATION
32 constexpr auto efiIoBusPciResAlloc = 0x0110;
33 constexpr auto efiSwDxeCorePcHandoffToNext = 0x0110;
34 constexpr auto efiSwPcUserSetup = 0x0700;
35 constexpr auto efiSwOsLoaderStart = 0x0180;
36 constexpr auto efiSwBsPcExitBootServices = 0x1910;
37 
38 void registerNetfnSBMRFunctions() __attribute__((constructor));
39 
40 namespace ipmi
41 {
42 
getSbmrBootProgressStage(uint8_t codeType,uint8_t codeSeverity,uint8_t codeClass,uint8_t codeSubClass,uint16_t codeOperation)43 std::string getSbmrBootProgressStage(uint8_t codeType, uint8_t codeSeverity,
44                                      uint8_t codeClass, uint8_t codeSubClass,
45                                      uint16_t codeOperation)
46 {
47     // Return OEM if code type or severity are unexpected
48     if (codeType != efiProgressCode || codeSeverity != efiCodeSeverityNone)
49     {
50         return bootProgressOem;
51     }
52 
53     // Code Class Software
54     if (codeClass == efiSoftware)
55     {
56         if (codeSubClass == efiSoftwareDxeCore &&
57             codeOperation == efiSwDxeCorePcHandoffToNext)
58         {
59             return bootProgressSystemInitComplete;
60         }
61         else if (codeSubClass == efiSoftwareDxeBsDriver &&
62                  codeOperation == efiSwPcUserSetup)
63         {
64             return bootProgressSystemSetup;
65         }
66         else if (codeSubClass == efiSoftwareDxeBsDriver &&
67                  codeOperation == efiSwOsLoaderStart)
68         {
69             return bootProgressOsStart;
70         }
71         else if (codeSubClass == efiSoftwareEfiBootService &&
72                  codeOperation == efiSwBsPcExitBootServices)
73         {
74             return bootProgressOsRuning;
75         }
76     }
77     // Code Class IO Bus
78     else if (codeClass == efiIoBus)
79     {
80         if (codeSubClass == efiIoBusPci && codeOperation == efiIoBusPciResAlloc)
81         {
82             return bootProgressPciInit;
83         }
84     }
85 
86     // Fallback to OEM if no conditions met
87     return bootProgressOem;
88 }
89 
updateBootProgressProperty(ipmi::Context::ptr & ctx,const std::string & value)90 bool updateBootProgressProperty(ipmi::Context::ptr& ctx,
91                                 const std::string& value)
92 {
93     std::string bootProgress =
94         "xyz.openbmc_project.State.Boot.Progress.ProgressStages." + value;
95     ipmi::DbusObjectInfo sbmrHostStateObject{};
96 
97     /* Get Host State Object */
98     boost::system::error_code ec =
99         ipmi::getDbusObject(ctx, sbmrHostStateIntf, sbmrHostStateObject);
100     if (ec.value())
101     {
102         lg2::error("Failed to get Host State object, Error={ERROR}", "ERROR",
103                    ec.message());
104         return false;
105     }
106 
107     /* Set Host State property */
108     ec = ipmi::setDbusProperty(ctx, sbmrHostStateObject.second,
109                                sbmrHostStateObject.first, sbmrHostStateIntf,
110                                "BootProgress", bootProgress);
111     if (ec.value())
112     {
113         lg2::error(
114             "updateBootProgressProperty, can't set progerty - Error={ERROR}",
115             "ERROR", ec.message());
116         return false;
117     }
118 
119     return true;
120 }
121 
updateBootProgressLastUpdateProperty(ipmi::Context::ptr & ctx,uint64_t timeStamp)122 bool updateBootProgressLastUpdateProperty(ipmi::Context::ptr& ctx,
123                                           uint64_t timeStamp)
124 {
125     ipmi::DbusObjectInfo sbmrHostStateObject{};
126 
127     /* Get Host State Object */
128     boost::system::error_code ec =
129         ipmi::getDbusObject(ctx, sbmrHostStateIntf, sbmrHostStateObject);
130     if (ec.value())
131     {
132         lg2::error("Failed to get Host State object, Error={ERROR}", "ERROR",
133                    ec.message());
134         return false;
135     }
136 
137     /* Set Host State property */
138     ec = ipmi::setDbusProperty(ctx, sbmrHostStateObject.second,
139                                sbmrHostStateObject.first, sbmrHostStateIntf,
140                                "BootProgressLastUpdate", timeStamp);
141     if (ec.value())
142     {
143         lg2::error(
144             "updateBootProgressLastUpdateProperty, can't set property - Error={ERROR}",
145             "ERROR", ec.message());
146         return false;
147     }
148 
149     return true;
150 }
151 
sendBootProgressCode(ipmi::Context::ptr ctx,uint8_t codeType,uint8_t codeReserved1,uint8_t codeReserved2,uint8_t codeSeverity,uint8_t codeOperation1,uint8_t codeOperation2,uint8_t codeSubClass,uint8_t codeClass,uint8_t instance)152 ipmi::RspType<> sendBootProgressCode(
153     ipmi::Context::ptr ctx, uint8_t codeType, uint8_t codeReserved1,
154     uint8_t codeReserved2, uint8_t codeSeverity, uint8_t codeOperation1,
155     uint8_t codeOperation2, uint8_t codeSubClass, uint8_t codeClass,
156     uint8_t instance)
157 {
158     /* Update boot progress code to Dbus property */
159     ipmi::DbusObjectInfo sbmrBootStateObject{};
160 
161     /* Get Boot State Object */
162     boost::system::error_code ec =
163         ipmi::getDbusObject(ctx, sbmrBootStateIntf, sbmrBootStateObject);
164     if (ec.value())
165     {
166         lg2::error("Failed to get Boot State object, Error={ERROR}", "ERROR",
167                    ec.message());
168         return ipmi::responseUnspecifiedError();
169     }
170 
171     /* Set Boot State property */
172     BootProgressCode bpCode(
173         {codeType, codeReserved1, codeReserved2, codeSeverity, codeOperation1,
174          codeOperation2, codeSubClass, codeClass, instance},
175         {});
176     ec = ipmi::setDbusProperty(ctx, sbmrBootStateObject.second,
177                                sbmrBootStateObject.first, sbmrBootStateIntf,
178                                "Value", bpCode);
179     if (ec.value())
180     {
181         lg2::error("Failed to set boot progress code, Error={ERROR}", "ERROR",
182                    ec.message());
183         return ipmi::responseUnspecifiedError();
184     }
185 
186     /* Update Redfish BootProgress object */
187     auto timeStamp = std::chrono::duration_cast<std::chrono::microseconds>(
188                          std::chrono::system_clock::now().time_since_epoch())
189                          .count();
190     if (!updateBootProgressLastUpdateProperty(ctx, timeStamp))
191     {
192         return ipmi::responseUnspecifiedError();
193     }
194 
195     /* Chek for BootProgressTypes */
196     uint16_t codeOperation =
197         static_cast<uint16_t>(codeOperation1) << 8 | codeOperation2;
198 
199     std::string stage = getSbmrBootProgressStage(
200         codeType, codeSeverity, codeClass, codeSubClass, codeOperation);
201 
202     if (!updateBootProgressProperty(ctx, stage))
203     {
204         return ipmi::responseUnspecifiedError();
205     }
206 
207     return ipmi::responseSuccess();
208 }
209 
210 ipmi::RspType<uint8_t, // STATUS_CODE_TYPE
211               uint8_t, // STATUS_CODE_RESERVED1
212               uint8_t, // STATUS_CODE_RESERVED2
213               uint8_t, // STATUS_CODE_SEVERITY
214               uint8_t, // STATUS_CODE_OPERATION1
215               uint8_t, // STATUS_CODE_OPERATION2
216               uint8_t, // STATUS_CODE_SUBCLASS
217               uint8_t, // STATUS_CODE_CLASS
218               uint8_t> // Instance
getBootProgressCode(ipmi::Context::ptr ctx)219     getBootProgressCode(ipmi::Context::ptr ctx)
220 {
221     ipmi::DbusObjectInfo sbmrBootStateObject{};
222 
223     /* Get Boot State Object */
224     boost::system::error_code ec =
225         ipmi::getDbusObject(ctx, sbmrBootStateIntf, sbmrBootStateObject);
226     if (ec.value())
227     {
228         lg2::error("Failed to get Boot State object, Error={ERROR}", "ERROR",
229                    ec.message());
230         return ipmi::responseUnspecifiedError();
231     }
232 
233     /* Get Boot State property */
234     BootProgressCode value;
235     ec = ipmi::getDbusProperty(ctx, sbmrBootStateObject.second,
236                                sbmrBootStateObject.first, sbmrBootStateIntf,
237                                "Value", value);
238     if (ec.value())
239     {
240         lg2::error("Can't get property Value, Error={ERROR}", "ERROR",
241                    ec.message());
242         return ipmi::responseUnspecifiedError();
243     }
244 
245     auto respBootProgressCode = std::get<0>(std::move(value));
246     if (respBootProgressCode.size() != sbmrBootProgressCodeSize)
247     {
248         return ipmi::responseUnspecifiedError();
249     }
250 
251     return ipmi::responseSuccess(
252         respBootProgressCode[0], respBootProgressCode[1],
253         respBootProgressCode[2], respBootProgressCode[3],
254         respBootProgressCode[4], respBootProgressCode[5],
255         respBootProgressCode[6], respBootProgressCode[7],
256         respBootProgressCode[8]);
257 }
258 
checkAllowedMediumType(uint8_t mediumType)259 bool checkAllowedMediumType(uint8_t mediumType)
260 {
261     if (mediumType ==
262             static_cast<uint8_t>(ipmi::EChannelMediumType::smbusV20) ||
263         mediumType ==
264             static_cast<uint8_t>(ipmi::EChannelMediumType::systemInterface) ||
265         mediumType == static_cast<uint8_t>(ipmi::EChannelMediumType::oem))
266     {
267         return true;
268     }
269 
270     return false;
271 }
272 
sbmrFilterCommands(ipmi::message::Request::ptr request)273 ipmi::Cc sbmrFilterCommands(ipmi::message::Request::ptr request)
274 {
275     if (request->ctx->netFn != ipmi::netFnGroup ||
276         request->ctx->group != ipmi::groupSBMR)
277     {
278         // Skip if not group SBMR
279         return ipmi::ccSuccess;
280     }
281 
282     ipmi::ChannelInfo chInfo;
283     if (ipmi::getChannelInfo(request->ctx->channel, chInfo) != ipmi::ccSuccess)
284     {
285         lg2::error("Failed to get Channel Info, channel={CHANNEL}", "CHANNEL",
286                    request->ctx->channel);
287         return ipmi::ccUnspecifiedError;
288     }
289 
290     if (request->ctx->cmd == ipmi::sbmr::cmdSendBootProgressCode &&
291         !checkAllowedMediumType(chInfo.mediumType))
292     {
293         lg2::error("Error - Medium interface not supported, medium={TYPE}",
294                    "TYPE", chInfo.mediumType);
295         return ipmi::ccCommandNotAvailable;
296     }
297 
298     return ipmi::ccSuccess;
299 }
300 
301 } // namespace ipmi
302 
registerNetfnSBMRFunctions()303 void registerNetfnSBMRFunctions()
304 {
305     registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupSBMR,
306                          ipmi::sbmr::cmdSendBootProgressCode,
307                          ipmi::Privilege::Admin, ipmi::sendBootProgressCode);
308 
309     registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupSBMR,
310                          ipmi::sbmr::cmdGetBootProgressCode,
311                          ipmi::Privilege::User, ipmi::getBootProgressCode);
312 
313     ipmi::registerFilter(ipmi::prioOemBase,
314                          [](ipmi::message::Request::ptr request) {
315                              return ipmi::sbmrFilterCommands(request);
316                          });
317 }
318