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