xref: /openbmc/pldm/libpldmresponder/bios.cpp (revision 1ac46403)
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 (const std::exception& e)
187     {
188         std::cerr << "Error Setting time,PATH=" << setTimePath
189                   << "TIME INTERFACE=" << setTimeInterface
190                   << "ERROR=" << e.what() << "\n";
191 
192         return ccOnlyResponse(request, PLDM_ERROR);
193     }
194 
195     return ccOnlyResponse(request, PLDM_SUCCESS);
196 }
197 
198 Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength)
199 {
200     uint32_t transferHandle{};
201     uint8_t transferOpFlag{};
202     uint8_t tableType{};
203 
204     auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle,
205                                         &transferOpFlag, &tableType);
206     if (rc != PLDM_SUCCESS)
207     {
208         return ccOnlyResponse(request, rc);
209     }
210 
211     auto table =
212         biosConfig.getBIOSTable(static_cast<pldm_bios_table_types>(tableType));
213     if (!table)
214     {
215         return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
216     }
217 
218     Response response(sizeof(pldm_msg_hdr) +
219                       PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + table->size());
220     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
221 
222     rc = encode_get_bios_table_resp(
223         request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */,
224         PLDM_START_AND_END, table->data(), response.size(), responsePtr);
225     if (rc != PLDM_SUCCESS)
226     {
227         return ccOnlyResponse(request, rc);
228     }
229 
230     return response;
231 }
232 
233 Response Handler::setBIOSTable(const pldm_msg* request, size_t payloadLength)
234 {
235     uint32_t transferHandle{};
236     uint8_t transferOpFlag{};
237     uint8_t tableType{};
238     struct variable_field field;
239 
240     auto rc = decode_set_bios_table_req(request, payloadLength, &transferHandle,
241                                         &transferOpFlag, &tableType, &field);
242     if (rc != PLDM_SUCCESS)
243     {
244         return ccOnlyResponse(request, rc);
245     }
246 
247     Table table(field.ptr, field.ptr + field.length);
248     rc = biosConfig.setBIOSTable(tableType, table);
249     if (rc != PLDM_SUCCESS)
250     {
251         return ccOnlyResponse(request, rc);
252     }
253 
254     Response response(sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_TABLE_RESP_BYTES);
255     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
256 
257     rc = encode_set_bios_table_resp(request->hdr.instance_id, PLDM_SUCCESS,
258                                     0 /* nxtTransferHandle */, responsePtr);
259     if (rc != PLDM_SUCCESS)
260     {
261         return ccOnlyResponse(request, rc);
262     }
263 
264     return response;
265 }
266 
267 Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request,
268                                                        size_t payloadLength)
269 {
270     uint32_t transferHandle;
271     uint8_t transferOpFlag;
272     uint16_t attributeHandle;
273 
274     auto rc = decode_get_bios_attribute_current_value_by_handle_req(
275         request, payloadLength, &transferHandle, &transferOpFlag,
276         &attributeHandle);
277     if (rc != PLDM_SUCCESS)
278     {
279         return ccOnlyResponse(request, rc);
280     }
281 
282     auto table = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
283     if (!table)
284     {
285         return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
286     }
287 
288     auto entry = pldm_bios_table_attr_value_find_by_handle(
289         table->data(), table->size(), attributeHandle);
290     if (entry == nullptr)
291     {
292         return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE);
293     }
294 
295     auto entryLength = pldm_bios_table_attr_value_entry_length(entry);
296     Response response(sizeof(pldm_msg_hdr) +
297                           PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES +
298                           entryLength,
299                       0);
300     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
301     rc = encode_get_bios_current_value_by_handle_resp(
302         request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
303         reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr);
304     if (rc != PLDM_SUCCESS)
305     {
306         return ccOnlyResponse(request, rc);
307     }
308 
309     return response;
310 }
311 
312 Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request,
313                                                size_t payloadLength)
314 {
315     uint32_t transferHandle;
316     uint8_t transferOpFlag;
317     variable_field attributeField;
318 
319     auto rc = decode_set_bios_attribute_current_value_req(
320         request, payloadLength, &transferHandle, &transferOpFlag,
321         &attributeField);
322     if (rc != PLDM_SUCCESS)
323     {
324         return ccOnlyResponse(request, rc);
325     }
326 
327     rc = biosConfig.setAttrValue(attributeField.ptr, attributeField.length);
328 
329     Response response(
330         sizeof(pldm_msg_hdr) + PLDM_SET_BIOS_ATTR_CURR_VAL_RESP_BYTES, 0);
331     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
332 
333     encode_set_bios_attribute_current_value_resp(request->hdr.instance_id, rc,
334                                                  0, responsePtr);
335 
336     return response;
337 }
338 
339 } // namespace bios
340 } // namespace responder
341 } // namespace pldm
342