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