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