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