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