xref: /openbmc/pldm/libpldmresponder/bios.cpp (revision d15fa099)
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, pldm::InstanceIdDb* instanceIdDb,
73                  pldm::requester::Handler<pldm::requester::Request>* handler) :
74     biosConfig(BIOS_JSONS_DIR, BIOS_TABLES_DIR, &dbusHandler, fd, eid,
75                instanceIdDb, 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(request,
99                                                           payloadLength);
100     });
101     handlers.emplace(PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE,
102                      [this](const pldm_msg* request, size_t payloadLength) {
103         return this->setBIOSAttributeCurrentValue(request, payloadLength);
104     });
105 }
106 
107 Response Handler::getDateTime(const pldm_msg* request, size_t /*payloadLength*/)
108 {
109     uint8_t seconds = 0;
110     uint8_t minutes = 0;
111     uint8_t hours = 0;
112     uint8_t day = 0;
113     uint8_t month = 0;
114     uint16_t year = 0;
115 
116     constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime";
117     constexpr auto bmcTimePath = "/xyz/openbmc_project/time/bmc";
118     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0);
119     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
120     EpochTimeUS timeUsec;
121 
122     try
123     {
124         timeUsec = pldm::utils::DBusHandler().getDbusProperty<EpochTimeUS>(
125             bmcTimePath, "Elapsed", timeInterface);
126     }
127     catch (const sdbusplus::exception_t& e)
128     {
129         error(
130             "Error getting time, PATH={BMC_TIME_PATH} TIME INTERACE={TIME_INTERFACE}",
131             "BMC_TIME_PATH", bmcTimePath, "TIME_INTERFACE", timeInterface);
132 
133         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR);
134     }
135 
136     uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>(
137                            std::chrono::microseconds(timeUsec))
138                            .count();
139 
140     pldm::responder::utils::epochToBCDTime(timeSec, seconds, minutes, hours,
141                                            day, month, year);
142 
143     auto rc = encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS,
144                                         seconds, minutes, hours, day, month,
145                                         year, responsePtr);
146     if (rc != PLDM_SUCCESS)
147     {
148         return ccOnlyResponse(request, rc);
149     }
150 
151     return response;
152 }
153 
154 Response Handler::setDateTime(const pldm_msg* request, size_t payloadLength)
155 {
156     uint8_t seconds = 0;
157     uint8_t minutes = 0;
158     uint8_t hours = 0;
159     uint8_t day = 0;
160     uint8_t month = 0;
161     uint16_t year = 0;
162     std::time_t timeSec;
163 
164     constexpr auto timeSyncPath = "/xyz/openbmc_project/time/sync_method";
165     constexpr auto timeSyncInterface =
166         "xyz.openbmc_project.Time.Synchronization";
167     constexpr auto timeSyncProperty = "TimeSyncMethod";
168 
169     // The time is correct on BMC when in NTP mode, so we do not want to
170     // try and set the time again and cause potential time drifts.
171     try
172     {
173         auto propVal = pldm::utils::DBusHandler().getDbusPropertyVariant(
174             timeSyncPath, timeSyncProperty, timeSyncInterface);
175         const auto& mode = std::get<std::string>(propVal);
176 
177         if (mode == "xyz.openbmc_project.Time.Synchronization.Method.NTP")
178         {
179             return ccOnlyResponse(request, PLDM_SUCCESS);
180         }
181     }
182     catch (const std::exception& e)
183     {
184         error(
185             "Error getting the time sync property, PATH={TIME_SYNC_PATH} INTERFACE={SYNC_INTERFACE} PROPERTY={SYNC_PROP} ERROR={ERR_EXCEP}",
186             "TIME_SYNC_PATH", timeSyncPath, "SYNC_INTERFACE", timeSyncInterface,
187             "SYNC_PROP", timeSyncProperty, "ERR_EXCEP", e.what());
188     }
189 
190     constexpr auto setTimeInterface = "xyz.openbmc_project.Time.EpochTime";
191     constexpr auto setTimePath = "/xyz/openbmc_project/time/bmc";
192     constexpr auto timeSetPro = "Elapsed";
193 
194     auto rc = decode_set_date_time_req(request, payloadLength, &seconds,
195                                        &minutes, &hours, &day, &month, &year);
196     if (rc != PLDM_SUCCESS)
197     {
198         return ccOnlyResponse(request, rc);
199     }
200     timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day,
201                                                   month, year);
202     uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>(
203                             std::chrono::seconds(timeSec))
204                             .count();
205     PropertyValue value{timeUsec};
206     try
207     {
208         DBusMapping dbusMapping{setTimePath, setTimeInterface, timeSetPro,
209                                 "uint64_t"};
210         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
211     }
212     catch (const std::exception& e)
213     {
214         error(
215             "Error Setting time,PATH={SET_TIME_PATH} TIME INTERFACE={TIME_INTERFACE} ERROR={ERR_EXCEP}",
216             "SET_TIME_PATH", setTimePath, "TIME_INTERFACE", setTimeInterface,
217             "ERR_EXCEP", e.what());
218         return ccOnlyResponse(request, PLDM_ERROR);
219     }
220 
221     return ccOnlyResponse(request, PLDM_SUCCESS);
222 }
223 
224 Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength)
225 {
226     uint32_t transferHandle{};
227     uint8_t transferOpFlag{};
228     uint8_t tableType{};
229 
230     auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle,
231                                         &transferOpFlag, &tableType);
232     if (rc != PLDM_SUCCESS)
233     {
234         return ccOnlyResponse(request, rc);
235     }
236 
237     auto table =
238         biosConfig.getBIOSTable(static_cast<pldm_bios_table_types>(tableType));
239     if (!table)
240     {
241         return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
242     }
243 
244     Response response(sizeof(pldm_msg_hdr) +
245                       PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + table->size());
246     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
247 
248     rc = encode_get_bios_table_resp(
249         request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */,
250         PLDM_START_AND_END, table->data(), response.size(), responsePtr);
251     if (rc != PLDM_SUCCESS)
252     {
253         return ccOnlyResponse(request, rc);
254     }
255 
256     return response;
257 }
258 
259 Response Handler::setBIOSTable(const pldm_msg* request, size_t payloadLength)
260 {
261     uint32_t transferHandle{};
262     uint8_t transferOpFlag{};
263     uint8_t tableType{};
264     struct variable_field field;
265 
266     auto rc = decode_set_bios_table_req(request, payloadLength, &transferHandle,
267                                         &transferOpFlag, &tableType, &field);
268     if (rc != PLDM_SUCCESS)
269     {
270         return ccOnlyResponse(request, rc);
271     }
272 
273     Table table(field.ptr, field.ptr + field.length);
274     rc = biosConfig.setBIOSTable(tableType, table);
275     if (rc != PLDM_SUCCESS)
276     {
277         return ccOnlyResponse(request, rc);
278     }
279 
280     Response response(sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_TABLE_RESP_BYTES);
281     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
282 
283     rc = encode_set_bios_table_resp(request->hdr.instance_id, PLDM_SUCCESS,
284                                     0 /* nxtTransferHandle */, responsePtr);
285     if (rc != PLDM_SUCCESS)
286     {
287         return ccOnlyResponse(request, rc);
288     }
289 
290     return response;
291 }
292 
293 Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request,
294                                                        size_t payloadLength)
295 {
296     uint32_t transferHandle;
297     uint8_t transferOpFlag;
298     uint16_t attributeHandle;
299 
300     auto rc = decode_get_bios_attribute_current_value_by_handle_req(
301         request, payloadLength, &transferHandle, &transferOpFlag,
302         &attributeHandle);
303     if (rc != PLDM_SUCCESS)
304     {
305         return ccOnlyResponse(request, rc);
306     }
307 
308     auto table = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
309     if (!table)
310     {
311         return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
312     }
313 
314     auto entry = pldm_bios_table_attr_value_find_by_handle(
315         table->data(), table->size(), attributeHandle);
316     if (entry == nullptr)
317     {
318         return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE);
319     }
320 
321     auto entryLength = pldm_bios_table_attr_value_entry_length(entry);
322     Response response(sizeof(pldm_msg_hdr) +
323                           PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES +
324                           entryLength,
325                       0);
326     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
327     rc = encode_get_bios_current_value_by_handle_resp(
328         request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
329         reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr);
330     if (rc != PLDM_SUCCESS)
331     {
332         return ccOnlyResponse(request, rc);
333     }
334 
335     return response;
336 }
337 
338 Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request,
339                                                size_t payloadLength)
340 {
341     uint32_t transferHandle;
342     uint8_t transferOpFlag;
343     variable_field attributeField;
344 
345     auto rc = decode_set_bios_attribute_current_value_req(
346         request, payloadLength, &transferHandle, &transferOpFlag,
347         &attributeField);
348     if (rc != PLDM_SUCCESS)
349     {
350         return ccOnlyResponse(request, rc);
351     }
352 
353     rc = biosConfig.setAttrValue(attributeField.ptr, attributeField.length,
354                                  false);
355 
356     Response response(
357         sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_ATTR_CURR_VAL_RESP_BYTES, 0);
358     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
359 
360     encode_set_bios_attribute_current_value_resp(request->hdr.instance_id, rc,
361                                                  0, responsePtr);
362 
363     return response;
364 }
365 
366 } // namespace bios
367 } // namespace responder
368 } // namespace pldm
369