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