xref: /openbmc/pldm/libpldmresponder/bios.cpp (revision 6a30a3e4)
1 #include "bios.hpp"
2 
3 #include "utils.hpp"
4 #include "xyz/openbmc_project/Common/error.hpp"
5 
6 #include <time.h>
7 
8 #include <array>
9 #include <boost/crc.hpp>
10 #include <chrono>
11 #include <ctime>
12 #include <filesystem>
13 #include <iostream>
14 #include <memory>
15 #include <numeric>
16 #include <stdexcept>
17 #include <string>
18 #include <variant>
19 #include <vector>
20 
21 namespace pldm
22 {
23 
24 namespace responder
25 {
26 
27 namespace utils
28 {
29 
30 void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes,
31                     uint8_t& hours, uint8_t& day, uint8_t& month,
32                     uint16_t& year)
33 {
34     auto t = time_t(timeSec);
35     auto time = localtime(&t);
36 
37     seconds = pldm::utils::decimalToBcd(time->tm_sec);
38     minutes = pldm::utils::decimalToBcd(time->tm_min);
39     hours = pldm::utils::decimalToBcd(time->tm_hour);
40     day = pldm::utils::decimalToBcd(time->tm_mday);
41     month = pldm::utils::decimalToBcd(time->tm_mon +
42                                       1); // The number of months in the range
43                                           // 0 to 11.PLDM expects range 1 to 12
44     year = pldm::utils::decimalToBcd(time->tm_year +
45                                      1900); // The number of years since 1900
46 }
47 
48 std::time_t timeToEpoch(uint8_t seconds, uint8_t minutes, uint8_t hours,
49                         uint8_t day, uint8_t month, uint16_t year)
50 {
51     struct std::tm stm;
52 
53     stm.tm_year = year - 1900;
54     stm.tm_mon = month - 1;
55     stm.tm_mday = day;
56     stm.tm_hour = hours;
57     stm.tm_min = minutes;
58     stm.tm_sec = seconds;
59     stm.tm_isdst = -1;
60 
61     // It will get the time in seconds since
62     // Epoch, 1970.1.1 00:00:00 +0000,UTC.
63     return timegm(&stm);
64 }
65 
66 } // namespace utils
67 
68 namespace bios
69 {
70 
71 using EpochTimeUS = uint64_t;
72 
73 DBusHandler dbusHandler;
74 
75 Handler::Handler() : biosConfig(BIOS_JSONS_DIR, BIOS_TABLES_DIR, &dbusHandler)
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_GET_BIOS_ATTRIBUTE_CURRENT_VALUE_BY_HANDLE,
93                      [this](const pldm_msg* request, size_t payloadLength) {
94                          return this->getBIOSAttributeCurrentValueByHandle(
95                              request, payloadLength);
96                      });
97     handlers.emplace(PLDM_SET_BIOS_ATTRIBUTE_CURRENT_VALUE,
98                      [this](const pldm_msg* request, size_t payloadLength) {
99                          return this->setBIOSAttributeCurrentValue(
100                              request, payloadLength);
101                      });
102 }
103 
104 Response Handler::getDateTime(const pldm_msg* request, size_t /*payloadLength*/)
105 {
106     uint8_t seconds = 0;
107     uint8_t minutes = 0;
108     uint8_t hours = 0;
109     uint8_t day = 0;
110     uint8_t month = 0;
111     uint16_t year = 0;
112 
113     constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime";
114     constexpr auto hostTimePath = "/xyz/openbmc_project/time/host";
115     Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0);
116     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
117     EpochTimeUS timeUsec;
118 
119     try
120     {
121         timeUsec = pldm::utils::DBusHandler().getDbusProperty<EpochTimeUS>(
122             hostTimePath, "Elapsed", timeInterface);
123     }
124     catch (const sdbusplus::exception::SdBusError& e)
125     {
126         std::cerr << "Error getting time, PATH=" << hostTimePath
127                   << " TIME INTERACE=" << timeInterface << "\n";
128 
129         return CmdHandler::ccOnlyResponse(request, PLDM_ERROR);
130     }
131 
132     uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>(
133                            std::chrono::microseconds(timeUsec))
134                            .count();
135 
136     pldm::responder::utils::epochToBCDTime(timeSec, seconds, minutes, hours,
137                                            day, month, year);
138 
139     auto rc = encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS,
140                                         seconds, minutes, hours, day, month,
141                                         year, responsePtr);
142     if (rc != PLDM_SUCCESS)
143     {
144         return ccOnlyResponse(request, rc);
145     }
146 
147     return response;
148 }
149 
150 Response Handler::setDateTime(const pldm_msg* request, size_t payloadLength)
151 {
152     uint8_t seconds = 0;
153     uint8_t minutes = 0;
154     uint8_t hours = 0;
155     uint8_t day = 0;
156     uint8_t month = 0;
157     uint16_t year = 0;
158     std::time_t timeSec;
159 
160     constexpr auto setTimeInterface = "xyz.openbmc_project.Time.EpochTime";
161     constexpr auto setTimePath = "/xyz/openbmc_project/time/host";
162     constexpr auto timeSetPro = "Elapsed";
163 
164     auto rc = decode_set_date_time_req(request, payloadLength, &seconds,
165                                        &minutes, &hours, &day, &month, &year);
166     if (rc != PLDM_SUCCESS)
167     {
168         return ccOnlyResponse(request, rc);
169     }
170     timeSec = pldm::responder::utils::timeToEpoch(seconds, minutes, hours, day,
171                                                   month, year);
172     uint64_t timeUsec = std::chrono::duration_cast<std::chrono::microseconds>(
173                             std::chrono::seconds(timeSec))
174                             .count();
175     PropertyValue value{timeUsec};
176     try
177     {
178         DBusMapping dbusMapping{setTimePath, setTimeInterface, timeSetPro,
179                                 "uint64_t"};
180         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
181     }
182     catch (std::exception& e)
183     {
184 
185         std::cerr << "Error Setting time,PATH=" << setTimePath
186                   << "TIME INTERFACE=" << setTimeInterface
187                   << "ERROR=" << e.what() << "\n";
188 
189         return ccOnlyResponse(request, PLDM_ERROR);
190     }
191 
192     return ccOnlyResponse(request, PLDM_SUCCESS);
193 }
194 
195 Response Handler::getBIOSTable(const pldm_msg* request, size_t payloadLength)
196 {
197     uint32_t transferHandle{};
198     uint8_t transferOpFlag{};
199     uint8_t tableType{};
200 
201     auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle,
202                                         &transferOpFlag, &tableType);
203     if (rc != PLDM_SUCCESS)
204     {
205         return ccOnlyResponse(request, rc);
206     }
207 
208     auto table =
209         biosConfig.getBIOSTable(static_cast<pldm_bios_table_types>(tableType));
210     if (!table)
211     {
212         return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
213     }
214 
215     Response response(sizeof(pldm_msg_hdr) +
216                       PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES + table->size());
217     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
218 
219     rc = encode_get_bios_table_resp(
220         request->hdr.instance_id, PLDM_SUCCESS, 0 /* nxtTransferHandle */,
221         PLDM_START_AND_END, table->data(), response.size(), responsePtr);
222     if (rc != PLDM_SUCCESS)
223     {
224         return ccOnlyResponse(request, rc);
225     }
226 
227     return response;
228 }
229 
230 Response Handler::getBIOSAttributeCurrentValueByHandle(const pldm_msg* request,
231                                                        size_t payloadLength)
232 {
233     uint32_t transferHandle;
234     uint8_t transferOpFlag;
235     uint16_t attributeHandle;
236 
237     auto rc = decode_get_bios_attribute_current_value_by_handle_req(
238         request, payloadLength, &transferHandle, &transferOpFlag,
239         &attributeHandle);
240     if (rc != PLDM_SUCCESS)
241     {
242         return ccOnlyResponse(request, rc);
243     }
244 
245     auto table = biosConfig.getBIOSTable(PLDM_BIOS_ATTR_VAL_TABLE);
246     if (!table)
247     {
248         return ccOnlyResponse(request, PLDM_BIOS_TABLE_UNAVAILABLE);
249     }
250 
251     auto entry = pldm_bios_table_attr_value_find_by_handle(
252         table->data(), table->size(), attributeHandle);
253     if (entry == nullptr)
254     {
255         return ccOnlyResponse(request, PLDM_INVALID_BIOS_ATTR_HANDLE);
256     }
257 
258     auto entryLength = pldm_bios_table_attr_value_entry_length(entry);
259     Response response(sizeof(pldm_msg_hdr) +
260                           PLDM_GET_BIOS_ATTR_CURR_VAL_BY_HANDLE_MIN_RESP_BYTES +
261                           entryLength,
262                       0);
263     auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
264     rc = encode_get_bios_current_value_by_handle_resp(
265         request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
266         reinterpret_cast<const uint8_t*>(entry), entryLength, responsePtr);
267     if (rc != PLDM_SUCCESS)
268     {
269         return ccOnlyResponse(request, rc);
270     }
271 
272     return response;
273 }
274 
275 Response Handler::setBIOSAttributeCurrentValue(const pldm_msg* request,
276                                                size_t payloadLength)
277 {
278     uint32_t transferHandle;
279     uint8_t transferOpFlag;
280     variable_field attributeField;
281 
282     auto rc = decode_set_bios_attribute_current_value_req(
283         request, payloadLength, &transferHandle, &transferOpFlag,
284         &attributeField);
285     if (rc != PLDM_SUCCESS)
286     {
287         return ccOnlyResponse(request, rc);
288     }
289 
290     rc = biosConfig.setAttrValue(attributeField.ptr, attributeField.length);
291 
292     return ccOnlyResponse(request, rc);
293 }
294 
295 } // namespace bios
296 } // namespace responder
297 } // namespace pldm
298