blob: 328d503c74667ba868344bcbbe125497f3127f52 [file] [log] [blame]
John Chung0a3f40b2025-01-22 18:07:52 -06001#include <ipmid/api.hpp>
2#include <ipmid/filter.hpp>
3#include <ipmid/utils.hpp>
4#include <phosphor-logging/lg2.hpp>
5
6constexpr auto sbmrBootStateIntf = "xyz.openbmc_project.State.Boot.Raw";
7constexpr auto sbmrHostStateIntf = "xyz.openbmc_project.State.Boot.Progress";
8constexpr auto sbmrBootProgressCodeSize = 9;
9
10constexpr auto bootProgressOem = "OEM";
11constexpr auto bootProgressOsRuning = "OSRunning";
12constexpr auto bootProgressOsStart = "OSStart";
13constexpr auto bootProgressPciInit = "PCIInit";
14constexpr auto bootProgressSystemInitComplete = "SystemInitComplete";
15constexpr auto bootProgressSystemSetup = "SystemSetup";
16
17// EFI_STATUS_CODE_TYPE
18constexpr auto efiProgressCode = 0x01;
19constexpr auto efiCodeSeverityNone = 0;
20
21// EFI_STATUS_CODE_CLASS
22constexpr auto efiIoBus = 0x02;
23constexpr auto efiSoftware = 0x03;
24
25// EFI_STATUS_CODE_SUBCLASS
26constexpr auto efiIoBusPci = 0x01;
27constexpr auto efiSoftwareDxeCore = 0x04;
28constexpr auto efiSoftwareDxeBsDriver = 0x05;
29constexpr auto efiSoftwareEfiBootService = 0x10;
30
31// EFI_STATUS_CODE_OPERATION
32constexpr auto efiIoBusPciResAlloc = 0x0110;
33constexpr auto efiSwDxeCorePcHandoffToNext = 0x0110;
34constexpr auto efiSwPcUserSetup = 0x0700;
35constexpr auto efiSwOsLoaderStart = 0x0180;
36constexpr auto efiSwBsPcExitBootServices = 0x1910;
37
38void registerNetfnSBMRFunctions() __attribute__((constructor));
39
40namespace ipmi
41{
42
43std::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
90bool 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
122bool 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
152ipmi::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
210ipmi::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
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
259bool 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
273ipmi::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
303void 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}