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