xref: /openbmc/pldm/libpldmresponder/bios.cpp (revision 2746fb48)
1 #include "bios.hpp"
2 
3 #include "common/utils.hpp"
4 
5 #include <time.h>
6 
7 #include <array>
8 #include <chrono>
9 #include <ctime>
10 #include <iostream>
11 #include <stdexcept>
12 #include <string>
13 #include <variant>
14 #include <vector>
15 
16 using namespace pldm::utils;
17 
18 namespace pldm
19 {
20 
21 namespace responder
22 {
23 
24 namespace utils
25 {
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(time->tm_mon +
39                                       1); // The number of months in the range
40                                           // 0 to 11.PLDM expects range 1 to 12
41     year = pldm::utils::decimalToBcd(time->tm_year +
42                                      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 
68 using EpochTimeUS = uint64_t;
69 
70 DBusHandler dbusHandler;
71 
72 Handler::Handler(int fd, uint8_t eid, dbus_api::Requester* requester,
73                  pldm::requester::Handler<pldm::requester::Request>* handler) :
74     biosConfig(BIOS_JSONS_DIR, BIOS_TABLES_DIR, &dbusHandler, fd, eid,
75                requester, 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(
99                              request, payloadLength);
100                      });
101     handlers.emplace(PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE,
102                      [this](const pldm_msg* request, size_t payloadLength) {
103                          return this->setBIOSAttributeCurrentValue(
104                              request, payloadLength);
105                      });
106 }
107 
108 Response Handler::getDateTime(const pldm_msg* request, size_t /*payloadLength*/)
109 {
110     uint8_t seconds = 0;
111     uint8_t minutes = 0;
112     uint8_t hours = 0;
113     uint8_t day = 0;
114     uint8_t month = 0;
115     uint16_t year = 0;
116 
117     constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime";
118     constexpr auto bmcTimePath = "/xyz/openbmc_project/time/bmc";
119     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0);
120     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
121     EpochTimeUS timeUsec;
122 
123     try
124     {
125         timeUsec = pldm::utils::DBusHandler().getDbusProperty<EpochTimeUS>(
126             bmcTimePath, "Elapsed", timeInterface);
127     }
128     catch (const sdbusplus::exception::exception& e)
129     {
130         std::cerr << "Error getting time, PATH=" << bmcTimePath
131                   << " TIME INTERACE=" << timeInterface << "\n";
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 setTimeInterface = "xyz.openbmc_project.Time.EpochTime";
165     constexpr auto setTimePath = "/xyz/openbmc_project/time/bmc";
166     constexpr auto timeSetPro = "Elapsed";
167 
168     auto rc = decode_set_date_time_req(request, payloadLength, &seconds,
169                                        &minutes, &hours, &day, &month, &year);
170     if (rc != PLDM_SUCCESS)
171     {
172         return ccOnlyResponse(request, rc);
173     }
174     timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day,
175                                                   month, year);
176     uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>(
177                             std::chrono::seconds(timeSec))
178                             .count();
179     PropertyValue value{timeUsec};
180     try
181     {
182         DBusMapping dbusMapping{setTimePath, setTimeInterface, timeSetPro,
183                                 "uint64_t"};
184         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
185     }
186     catch (std::exception& e)
187     {
188 
189         std::cerr << "Error Setting time,PATH=" << setTimePath
190                   << "TIME INTERFACE=" << setTimeInterface
191                   << "ERROR=" << e.what() << "\n";
192 
193         return ccOnlyResponse(request, PLDM_ERROR);
194     }
195 
196     return ccOnlyResponse(request, PLDM_SUCCESS);
197 }
198 
199 Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength)
200 {
201     uint32_t transferHandle{};
202     uint8_t transferOpFlag{};
203     uint8_t tableType{};
204 
205     auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle,
206                                         &transferOpFlag, &tableType);
207     if (rc != PLDM_SUCCESS)
208     {
209         return ccOnlyResponse(request, rc);
210     }
211 
212     auto table =
213         biosConfig.getBIOSTable(static_cast<pldm_bios_table_types>(tableType));
214     if (!table)
215     {
216         return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
217     }
218 
219     Response response(sizeof(pldm_msg_hdr) +
220                       PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + table->size());
221     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
222 
223     rc = encode_get_bios_table_resp(
224         request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */,
225         PLDM_START_AND_END, table->data(), response.size(), responsePtr);
226     if (rc != PLDM_SUCCESS)
227     {
228         return ccOnlyResponse(request, rc);
229     }
230 
231     return response;
232 }
233 
234 Response Handler::setBIOSTable(const pldm_msg* request, size_t payloadLength)
235 {
236     uint32_t transferHandle{};
237     uint8_t transferOpFlag{};
238     uint8_t tableType{};
239     struct variable_field field;
240 
241     auto rc = decode_set_bios_table_req(request, payloadLength, &transferHandle,
242                                         &transferOpFlag, &tableType, &field);
243     if (rc != PLDM_SUCCESS)
244     {
245         return ccOnlyResponse(request, rc);
246     }
247 
248     Table table(field.ptr, field.ptr + field.length);
249     rc = biosConfig.setBIOSTable(tableType, table);
250     if (rc != PLDM_SUCCESS)
251     {
252         return ccOnlyResponse(request, rc);
253     }
254 
255     Response response(sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_TABLE_RESP_BYTES);
256     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
257 
258     rc = encode_set_bios_table_resp(request->hdr.instance_id, PLDM_SUCCESS,
259                                     0 /* nxtTransferHandle */, responsePtr);
260     if (rc != PLDM_SUCCESS)
261     {
262         return ccOnlyResponse(request, rc);
263     }
264 
265     return response;
266 }
267 
268 Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request,
269                                                        size_t payloadLength)
270 {
271     uint32_t transferHandle;
272     uint8_t transferOpFlag;
273     uint16_t attributeHandle;
274 
275     auto rc = decode_get_bios_attribute_current_value_by_handle_req(
276         request, payloadLength, &transferHandle, &transferOpFlag,
277         &attributeHandle);
278     if (rc != PLDM_SUCCESS)
279     {
280         return ccOnlyResponse(request, rc);
281     }
282 
283     auto table = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
284     if (!table)
285     {
286         return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
287     }
288 
289     auto entry = pldm_bios_table_attr_value_find_by_handle(
290         table->data(), table->size(), attributeHandle);
291     if (entry == nullptr)
292     {
293         return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE);
294     }
295 
296     auto entryLength = pldm_bios_table_attr_value_entry_length(entry);
297     Response response(sizeof(pldm_msg_hdr) +
298                           PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES +
299                           entryLength,
300                       0);
301     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
302     rc = encode_get_bios_current_value_by_handle_resp(
303         request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
304         reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr);
305     if (rc != PLDM_SUCCESS)
306     {
307         return ccOnlyResponse(request, rc);
308     }
309 
310     return response;
311 }
312 
313 Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request,
314                                                size_t payloadLength)
315 {
316     uint32_t transferHandle;
317     uint8_t transferOpFlag;
318     variable_field attributeField;
319 
320     auto rc = decode_set_bios_attribute_current_value_req(
321         request, payloadLength, &transferHandle, &transferOpFlag,
322         &attributeField);
323     if (rc != PLDM_SUCCESS)
324     {
325         return ccOnlyResponse(request, rc);
326     }
327 
328     rc = biosConfig.setAttrValue(attributeField.ptr, attributeField.length);
329 
330     Response response(
331         sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_ATTR_CURR_VAL_RESP_BYTES, 0);
332     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
333 
334     encode_set_bios_attribute_current_value_resp(request->hdr.instance_id, rc,
335                                                  0, responsePtr);
336 
337     return response;
338 }
339 
340 } // namespace bios
341 } // namespace responder
342 } // namespace pldm
343