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