xref: /openbmc/pldm/libpldmresponder/bios.cpp (revision 1976152a7c7b26bb0991f38da4d11cf143ccf18e)
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