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