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