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