1 #include "bios.hpp"
2
3 #include "common/utils.hpp"
4
5 #include <phosphor-logging/lg2.hpp>
6
7 #include <array>
8 #include <chrono>
9 #include <ctime>
10 #include <stdexcept>
11 #include <string>
12 #include <variant>
13 #include <vector>
14
15 PHOSPHOR_LOG2_USING;
16
17 using namespace pldm::utils;
18
19 namespace pldm
20 {
21 namespace responder
22 {
23 namespace utils
24 {
epochToBCDTime(uint64_t timeSec,uint8_t & seconds,uint8_t & minutes,uint8_t & hours,uint8_t & day,uint8_t & month,uint16_t & year)25 void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes,
26 uint8_t& hours, uint8_t& day, uint8_t& month,
27 uint16_t& year)
28 {
29 auto t = time_t(timeSec);
30 auto time = localtime(&t);
31
32 seconds = pldm::utils::decimalToBcd(time->tm_sec);
33 minutes = pldm::utils::decimalToBcd(time->tm_min);
34 hours = pldm::utils::decimalToBcd(time->tm_hour);
35 day = pldm::utils::decimalToBcd(time->tm_mday);
36 month = pldm::utils::decimalToBcd(
37 time->tm_mon + 1); // The number of months in the range
38 // 0 to 11.PLDM expects range 1 to 12
39 year = pldm::utils::decimalToBcd(
40 time->tm_year + 1900); // The number of years since 1900
41 }
42
timeToEpoch(uint8_t seconds,uint8_t minutes,uint8_t hours,uint8_t day,uint8_t month,uint16_t year)43 std::time_t timeToEpoch(uint8_t seconds, uint8_t minutes, uint8_t hours,
44 uint8_t day, uint8_t month, uint16_t year)
45 {
46 struct std::tm stm;
47
48 stm.tm_year = year - 1900;
49 stm.tm_mon = month - 1;
50 stm.tm_mday = day;
51 stm.tm_hour = hours;
52 stm.tm_min = minutes;
53 stm.tm_sec = seconds;
54 stm.tm_isdst = -1;
55
56 // It will get the time in seconds since
57 // Epoch, 1970.1.1 00:00:00 +0000,UTC.
58 return timegm(&stm);
59 }
60
61 } // namespace utils
62
63 namespace bios
64 {
65 using EpochTimeUS = uint64_t;
66
67 DBusHandler dbusHandler;
68
Handler(int fd,uint8_t eid,pldm::InstanceIdDb * instanceIdDb,pldm::requester::Handler<pldm::requester::Request> * handler,pldm::responder::platform_config::Handler * platformConfigHandler,pldm::responder::bios::Callback requestPLDMServiceName)69 Handler::Handler(
70 int fd, uint8_t eid, pldm::InstanceIdDb* instanceIdDb,
71 pldm::requester::Handler<pldm::requester::Request>* handler,
72 pldm::responder::platform_config::Handler* platformConfigHandler,
73 pldm::responder::bios::Callback requestPLDMServiceName) :
74 biosConfig(BIOS_JSONS_DIR, BIOS_TABLES_DIR, &dbusHandler, fd, eid,
75 instanceIdDb, handler, platformConfigHandler,
76 requestPLDMServiceName)
77 {
78 handlers.emplace(
79 PLDM_SET_DATE_TIME,
80 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) {
81 return this->setDateTime(request, payloadLength);
82 });
83 handlers.emplace(
84 PLDM_GET_DATE_TIME,
85 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) {
86 return this->getDateTime(request, payloadLength);
87 });
88 handlers.emplace(
89 PLDM_GET_BIOS_TABLE,
90 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) {
91 return this->getBIOSTable(request, payloadLength);
92 });
93 handlers.emplace(
94 PLDM_SET_BIOS_TABLE,
95 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) {
96 return this->setBIOSTable(request, payloadLength);
97 });
98 handlers.emplace(
99 PLDM_GET_BIOS_ATTRIBUTE_CURRENT_VALUE_BY_HANDLE,
100 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) {
101 return this->getBIOSAttributeCurrentValueByHandle(request,
102 payloadLength);
103 });
104 handlers.emplace(
105 PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE,
106 [this](pldm_tid_t, const pldm_msg* request, size_t payloadLength) {
107 return this->setBIOSAttributeCurrentValue(request, payloadLength);
108 });
109 }
110
getDateTime(const pldm_msg * request,size_t)111 Response Handler::getDateTime(const pldm_msg* request, size_t /*payloadLength*/)
112 {
113 uint8_t seconds = 0;
114 uint8_t minutes = 0;
115 uint8_t hours = 0;
116 uint8_t day = 0;
117 uint8_t month = 0;
118 uint16_t year = 0;
119
120 constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime";
121 constexpr auto bmcTimePath = "/xyz/openbmc_project/time/bmc";
122 Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0);
123 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
124 EpochTimeUS timeUsec;
125
126 try
127 {
128 timeUsec = pldm::utils::DBusHandler().getDbusProperty<EpochTimeUS>(
129 bmcTimePath, "Elapsed", timeInterface);
130 }
131 catch (const sdbusplus::exception_t& e)
132 {
133 error(
134 "Error getting time from Elapsed property at path '{PATH}' on interface '{INTERFACE}': {ERROR}",
135 "PATH", bmcTimePath, "INTERFACE", timeInterface, "ERROR", e);
136 return CmdHandler::ccOnlyResponse(request, PLDM_ERROR);
137 }
138
139 uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>(
140 std::chrono::microseconds(timeUsec))
141 .count();
142
143 pldm::responder::utils::epochToBCDTime(timeSec, seconds, minutes, hours,
144 day, month, year);
145
146 auto rc = encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS,
147 seconds, minutes, hours, day, month,
148 year, responsePtr);
149 if (rc != PLDM_SUCCESS)
150 {
151 return ccOnlyResponse(request, rc);
152 }
153
154 return response;
155 }
156
setDateTime(const pldm_msg * request,size_t payloadLength)157 Response Handler::setDateTime(const pldm_msg* request, size_t payloadLength)
158 {
159 uint8_t seconds = 0;
160 uint8_t minutes = 0;
161 uint8_t hours = 0;
162 uint8_t day = 0;
163 uint8_t month = 0;
164 uint16_t year = 0;
165 std::time_t timeSec;
166
167 constexpr auto timeSyncPath = "/xyz/openbmc_project/time/sync_method";
168 constexpr auto timeSyncInterface =
169 "xyz.openbmc_project.Time.Synchronization";
170 constexpr auto timeSyncProperty = "TimeSyncMethod";
171
172 // The time is correct on BMC when in NTP mode, so we do not want to
173 // try and set the time again and cause potential time drifts.
174 try
175 {
176 auto propVal = pldm::utils::DBusHandler().getDbusPropertyVariant(
177 timeSyncPath, timeSyncProperty, timeSyncInterface);
178 const auto& mode = std::get<std::string>(propVal);
179
180 if (mode == "xyz.openbmc_project.Time.Synchronization.Method.NTP")
181 {
182 return ccOnlyResponse(request, PLDM_SUCCESS);
183 }
184 }
185 catch (const std::exception& e)
186 {
187 error(
188 "Failed to get the time sync property from path {TIME_SYNC_PATH}, interface '{SYNC_INTERFACE}' and property '{SYNC_PROPERTY}', error - '{ERROR}'",
189 "TIME_SYNC_PATH", timeSyncPath, "SYNC_INTERFACE", timeSyncInterface,
190 "SYNC_PROPERTY", timeSyncProperty, "ERROR", e);
191 }
192
193 constexpr auto setTimeInterface = "xyz.openbmc_project.Time.EpochTime";
194 constexpr auto setTimePath = "/xyz/openbmc_project/time/bmc";
195 constexpr auto timeSetPro = "Elapsed";
196
197 auto rc = decode_set_date_time_req(request, payloadLength, &seconds,
198 &minutes, &hours, &day, &month, &year);
199 if (rc != PLDM_SUCCESS)
200 {
201 return ccOnlyResponse(request, rc);
202 }
203 timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day,
204 month, year);
205 uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>(
206 std::chrono::seconds(timeSec))
207 .count();
208 PropertyValue value{timeUsec};
209 try
210 {
211 DBusMapping dbusMapping{setTimePath, setTimeInterface, timeSetPro,
212 "uint64_t"};
213 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
214 }
215 catch (const std::exception& e)
216 {
217 error(
218 "Failed to set time at {SET_TIME_PATH}, interface '{TIME_INTERFACE}' and error - {ERROR}",
219 "SET_TIME_PATH", setTimePath, "TIME_INTERFACE", setTimeInterface,
220 "ERROR", e);
221 return ccOnlyResponse(request, PLDM_ERROR);
222 }
223
224 return ccOnlyResponse(request, PLDM_SUCCESS);
225 }
226
getBIOSTable(const pldm_msg * request,size_t payloadLength)227 Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength)
228 {
229 uint32_t transferHandle{};
230 uint8_t transferOpFlag{};
231 uint8_t tableType{};
232
233 auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle,
234 &transferOpFlag, &tableType);
235 if (rc != PLDM_SUCCESS)
236 {
237 return ccOnlyResponse(request, rc);
238 }
239
240 auto table =
241 biosConfig.getBIOSTable(static_cast<pldm_bios_table_types>(tableType));
242 if (!table)
243 {
244 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
245 }
246
247 Response response(sizeof(pldm_msg_hdr) +
248 PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + table->size());
249 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
250
251 rc = encode_get_bios_table_resp(
252 request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */,
253 PLDM_START_AND_END, table->data(), response.size(), responsePtr);
254 if (rc != PLDM_SUCCESS)
255 {
256 return ccOnlyResponse(request, rc);
257 }
258
259 return response;
260 }
261
setBIOSTable(const pldm_msg * request,size_t payloadLength)262 Response Handler::setBIOSTable(const pldm_msg* request, size_t payloadLength)
263 {
264 uint32_t transferHandle{};
265 uint8_t transferOpFlag{};
266 uint8_t tableType{};
267 struct variable_field field;
268
269 auto rc = decode_set_bios_table_req(request, payloadLength, &transferHandle,
270 &transferOpFlag, &tableType, &field);
271 if (rc != PLDM_SUCCESS)
272 {
273 return ccOnlyResponse(request, rc);
274 }
275
276 Table table(field.ptr, field.ptr + field.length);
277 rc = biosConfig.setBIOSTable(tableType, table);
278 if (rc != PLDM_SUCCESS)
279 {
280 return ccOnlyResponse(request, rc);
281 }
282
283 Response response(sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_TABLE_RESP_BYTES);
284 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
285
286 rc = encode_set_bios_table_resp(request->hdr.instance_id, PLDM_SUCCESS,
287 0 /* nxtTransferHandle */, responsePtr);
288 if (rc != PLDM_SUCCESS)
289 {
290 return ccOnlyResponse(request, rc);
291 }
292
293 return response;
294 }
295
getBIOSAttributeCurrentValueByHandle(const pldm_msg * request,size_t payloadLength)296 Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request,
297 size_t payloadLength)
298 {
299 uint32_t transferHandle;
300 uint8_t transferOpFlag;
301 uint16_t attributeHandle;
302
303 auto rc = decode_get_bios_attribute_current_value_by_handle_req(
304 request, payloadLength, &transferHandle, &transferOpFlag,
305 &attributeHandle);
306 if (rc != PLDM_SUCCESS)
307 {
308 return ccOnlyResponse(request, rc);
309 }
310
311 auto table = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
312 if (!table)
313 {
314 return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
315 }
316
317 auto entry = pldm_bios_table_attr_value_find_by_handle(
318 table->data(), table->size(), attributeHandle);
319 if (entry == nullptr)
320 {
321 return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE);
322 }
323
324 auto entryLength = pldm_bios_table_attr_value_entry_length(entry);
325 Response response(sizeof(pldm_msg_hdr) +
326 PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES +
327 entryLength,
328 0);
329 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
330 rc = encode_get_bios_current_value_by_handle_resp(
331 request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
332 reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr);
333 if (rc != PLDM_SUCCESS)
334 {
335 return ccOnlyResponse(request, rc);
336 }
337
338 return response;
339 }
340
setBIOSAttributeCurrentValue(const pldm_msg * request,size_t payloadLength)341 Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request,
342 size_t payloadLength)
343 {
344 uint32_t transferHandle;
345 uint8_t transferOpFlag;
346 variable_field attributeField;
347
348 auto rc = decode_set_bios_attribute_current_value_req(
349 request, payloadLength, &transferHandle, &transferOpFlag,
350 &attributeField);
351 if (rc != PLDM_SUCCESS)
352 {
353 return ccOnlyResponse(request, rc);
354 }
355
356 rc = biosConfig.setAttrValue(attributeField.ptr, attributeField.length,
357 false);
358
359 Response response(
360 sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_ATTR_CURR_VAL_RESP_BYTES, 0);
361 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
362
363 encode_set_bios_attribute_current_value_resp(request->hdr.instance_id, rc,
364 0, responsePtr);
365
366 return response;
367 }
368
369 } // namespace bios
370 } // namespace responder
371 } // namespace pldm
372