blob: a6f07d8de1b27f3f75fb10170aa05797c1e2d8a6 [file] [log] [blame]
Tom Joseph52552ef2019-06-20 09:50:15 +05301#include "bios_parser.hpp"
2
3#include "libpldmresponder/utils.hpp"
4
5#include <filesystem>
6#include <fstream>
7#include <nlohmann/json.hpp>
8#include <optional>
9#include <phosphor-logging/log.hpp>
10
11namespace bios_parser
12{
13
14using Json = nlohmann::json;
15namespace fs = std::filesystem;
16using namespace phosphor::logging;
Tom Joseph52552ef2019-06-20 09:50:15 +053017
Carol Wang612f35b2019-08-26 17:14:26 +080018namespace
19{
20
21const std::vector<Json> emptyJsonList{};
22const Json emptyJson{};
23
24} // namespace
25
26int parseBiosJsonFile(const char* dirPath, const std::string& fileName,
27 Json& fileData)
28{
29 int rc = 0;
30
31 fs::path filePath(dirPath);
32 filePath /= fileName;
33
34 std::ifstream jsonFile(filePath);
35 if (!jsonFile.is_open())
36 {
37 log<level::ERR>("BIOS config file does not exist",
38 entry("FILE=%s", filePath.c_str()));
39 rc = -1;
40 }
41 else
42 {
43 fileData = Json::parse(jsonFile, nullptr, false);
44 if (fileData.is_discarded())
45 {
46 log<level::ERR>("Parsing config file failed",
47 entry("FILE=%s", filePath.c_str()));
48 rc = -1;
49 }
50 }
51
52 return rc;
53}
54
Tom Joseph52552ef2019-06-20 09:50:15 +053055namespace bios_enum
56{
57
58namespace internal
59{
60
61using PropertyValue =
62 std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t,
63 uint64_t, double, std::string>;
64using Value = std::string;
65
66/** @struct DBusMapping
67 *
68 * Data structure for storing information regarding BIOS enumeration attribute
69 * and the D-Bus object for the attribute.
70 */
71struct DBusMapping
72{
73 std::string objectPath; //!< D-Bus object path
74 std::string interface; //!< D-Bus interface
75 std::string propertyName; //!< D-Bus property name
76 std::map<PropertyValue, Value>
77 dBusValToValMap; //!< Map of D-Bus property
78 //!< value to attribute value
79};
80
81/** @brief Map containing the possible and the default values for the BIOS
82 * enumeration type attributes.
83 */
84AttrValuesMap valueMap;
85
86/** @brief Map containing the optional D-Bus property information about the
87 * BIOS enumeration type attributes.
88 */
89std::map<AttrName, std::optional<DBusMapping>> attrLookup;
90
91/** @brief Populate the mapping between D-Bus property value and attribute value
92 * for the BIOS enumeration attribute.
93 *
94 * @param[in] type - type of the D-Bus property
95 * @param[in] dBusValues - json array of D-Bus property values
96 * @param[in] pv - Possible values for the BIOS enumeration attribute
97 * @param[out] mapping - D-Bus mapping object for the attribute
98 *
99 */
100void populateMapping(const std::string& type, const Json& dBusValues,
101 const PossibleValues& pv, DBusMapping& mapping)
102{
103 size_t pos = 0;
104 PropertyValue value;
105 for (auto it = dBusValues.begin(); it != dBusValues.end(); ++it, ++pos)
106 {
107 if (type == "uint8_t")
108 {
109 value = static_cast<uint8_t>(it.value());
110 }
111 else if (type == "uint16_t")
112 {
113 value = static_cast<uint16_t>(it.value());
114 }
115 else if (type == "uint32_t")
116 {
117 value = static_cast<uint32_t>(it.value());
118 }
119 else if (type == "uint64_t")
120 {
121 value = static_cast<uint64_t>(it.value());
122 }
123 else if (type == "int16_t")
124 {
125 value = static_cast<int16_t>(it.value());
126 }
127 else if (type == "int32_t")
128 {
129 value = static_cast<int32_t>(it.value());
130 }
131 else if (type == "int64_t")
132 {
133 value = static_cast<int64_t>(it.value());
134 }
135 else if (type == "bool")
136 {
137 value = static_cast<bool>(it.value());
138 }
139 else if (type == "double")
140 {
141 value = static_cast<double>(it.value());
142 }
143 else if (type == "string")
144 {
145 value = static_cast<std::string>(it.value());
146 }
147 else
148 {
149 log<level::ERR>("Unknown D-Bus property type",
150 entry("TYPE=%s", type.c_str()));
151 }
152
153 mapping.dBusValToValMap.emplace(value, pv[pos]);
154 }
155}
156
157/** @brief Read the possible values for the BIOS enumeration type
158 *
159 * @param[in] possibleValues - json array of BIOS enumeration possible values
160 */
161PossibleValues readPossibleValues(Json& possibleValues)
162{
163 Strings biosStrings{};
164
165 for (auto& val : possibleValues)
166 {
167 biosStrings.emplace_back(std::move(val));
168 }
169
170 return biosStrings;
171}
172
173} // namespace internal
174
175int setupValueLookup(const char* dirPath)
176{
177 int rc = 0;
178
179 if (!internal::valueMap.empty() && !internal::attrLookup.empty())
180 {
181 return rc;
182 }
183
184 // Parse the BIOS enumeration config file
185 fs::path filePath(dirPath);
186 filePath /= bIOSEnumJson;
187
188 std::ifstream jsonFile(filePath);
189 if (!jsonFile.is_open())
190 {
191 log<level::ERR>("BIOS enum config file does not exist",
192 entry("FILE=%s", filePath.c_str()));
193 rc = -1;
194 return rc;
195 }
196
197 auto fileData = Json::parse(jsonFile, nullptr, false);
198 if (fileData.is_discarded())
199 {
200 log<level::ERR>("Parsing config file failed");
201 rc = -1;
202 return rc;
203 }
204
Carol Wang612f35b2019-08-26 17:14:26 +0800205 auto entries = fileData.value("entries", emptyJsonList);
Tom Joseph52552ef2019-06-20 09:50:15 +0530206 // Iterate through each JSON object in the config file
207 for (const auto& entry : entries)
208 {
209 std::string attr = entry.value("attribute_name", "");
210 PossibleValues possibleValues;
211 DefaultValues defaultValues;
212
213 Json pv = entry["possible_values"];
214 for (auto& val : pv)
215 {
216 possibleValues.emplace_back(std::move(val));
217 }
218
219 Json dv = entry["default_values"];
220 for (auto& val : dv)
221 {
222 defaultValues.emplace_back(std::move(val));
223 }
224
225 std::optional<internal::DBusMapping> dBusMap = std::nullopt;
Carol Wang612f35b2019-08-26 17:14:26 +0800226
Tom Joseph52552ef2019-06-20 09:50:15 +0530227 if (entry.count("dbus") != 0)
228 {
Carol Wang612f35b2019-08-26 17:14:26 +0800229 auto dBusEntry = entry.value("dbus", emptyJson);
Tom Joseph52552ef2019-06-20 09:50:15 +0530230 dBusMap = std::make_optional<internal::DBusMapping>();
231 dBusMap.value().objectPath = dBusEntry.value("object_path", "");
232 dBusMap.value().interface = dBusEntry.value("interface", "");
233 dBusMap.value().propertyName = dBusEntry.value("property_name", "");
234 std::string propType = dBusEntry.value("property_type", "");
235 Json propValues = dBusEntry["property_values"];
236 internal::populateMapping(propType, propValues, possibleValues,
237 dBusMap.value());
238 }
239
240 internal::attrLookup.emplace(attr, std::move(dBusMap));
241
242 // Defaulting all the types of attributes to BIOSEnumeration
243 internal::valueMap.emplace(
244 std::move(attr), std::make_tuple(false, std::move(possibleValues),
245 std::move(defaultValues)));
246 }
247
248 return rc;
249}
250
251const AttrValuesMap& getValues()
252{
253 return internal::valueMap;
254}
255
256CurrentValues getAttrValue(const AttrName& attrName)
257{
258 const auto& dBusMap = internal::attrLookup.at(attrName);
259 CurrentValues currentValues;
260 internal::PropertyValue propValue;
261
262 if (dBusMap == std::nullopt)
263 {
264 const auto& valueEntry = internal::valueMap.at(attrName);
265 const auto& [readOnly, possibleValues, currentValues] = valueEntry;
266 return currentValues;
267 }
268
269 auto bus = sdbusplus::bus::new_default();
270 auto service = pldm::responder::getService(bus, dBusMap.value().objectPath,
271 dBusMap.value().interface);
272 auto method =
273 bus.new_method_call(service.c_str(), dBusMap.value().objectPath.c_str(),
274 "org.freedesktop.DBus.Properties", "Get");
275 method.append(dBusMap.value().interface, dBusMap.value().propertyName);
276 auto reply = bus.call(method);
277 reply.read(propValue);
278
279 auto iter = dBusMap.value().dBusValToValMap.find(propValue);
280 if (iter != dBusMap.value().dBusValToValMap.end())
281 {
282 currentValues.push_back(iter->second);
283 }
284
285 return currentValues;
286}
287
288} // namespace bios_enum
289
Carol Wang612f35b2019-08-26 17:14:26 +0800290namespace bios_string
291{
292
293/** @brief BIOS string types
294 */
295enum BiosStringEncoding
296{
297 UNKNOWN = 0x00,
298 ASCII = 0x01,
299 HEX = 0x02,
300 UTF_8 = 0x03,
301 UTF_16LE = 0x04,
302 UTF_16BE = 0x05,
303 VENDOR_SPECIFIC = 0xFF
304};
305
306const std::map<std::string, uint8_t> strTypeMap{
307 {"Unknown", UNKNOWN},
308 {"ASCII", ASCII},
309 {"Hex", HEX},
310 {"UTF-8", UTF_8},
311 {"UTF-16LE", UTF_16LE},
312 {"UTF-16LE", UTF_16LE},
313 {"Vendor Specific", VENDOR_SPECIFIC}};
314
315namespace internal
316{
317
318/** @struct DBusMapping
319 *
320 * Data structure for storing information regarding BIOS string attribute
321 * and the D-Bus object for the attribute.
322 */
323struct DBusMapping
324{
325 std::string objectPath; //!< D-Bus object path
326 std::string interface; //!< D-Bus interface
327 std::string propertyName; //!< D-Bus property name
328};
329
330/** @brief Map containing the possible and the default values for the BIOS
331 * string type attributes.
332 */
333AttrValuesMap valueMap;
334
335/** @brief Map containing the optional D-Bus property information about the
336 * BIOS string type attributes.
337 */
338std::map<AttrName, std::optional<DBusMapping>> attrLookup;
339
340} // namespace internal
341
342int setupValueLookup(const char* dirPath)
343{
344 int rc = 0;
345
346 if (!internal::valueMap.empty() && !internal::attrLookup.empty())
347 {
348 return rc;
349 }
350
351 Json fileData;
352 rc = parseBiosJsonFile(dirPath, bIOSStrJson, fileData);
353 if (rc != 0)
354 {
355 return rc;
356 }
357
358 auto entries = fileData.value("entries", emptyJsonList);
359 // Iterate through each JSON object in the config file
360 for (const auto& entry : entries)
361 {
362 std::string attr = entry.value("attribute_name", "");
363 // Transfer string type from string to enum
364 std::string strTypeTmp = entry.value("string_type", "Unknown");
365 auto iter = strTypeMap.find(strTypeTmp);
366 if (iter == strTypeMap.end())
367 {
368 log<level::ERR>(
369 "Wrong string type",
370 phosphor::logging::entry("STRING_TYPE=%s", strTypeTmp.c_str()),
371 phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
372 return -1;
373 }
374 uint8_t strType = iter->second;
375
376 uint16_t minStrLen = entry.value("minimum_string_length", 0);
377 uint16_t maxStrLen = entry.value("maximum_string_length", 0);
378 if (maxStrLen - minStrLen < 0)
379 {
380 log<level::ERR>(
381 "Maximum string length is smaller than minimum string length",
382 phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
383 return -1;
384 }
385 uint16_t defaultStrLen = entry.value("default_string_length", 0);
386 std::string defaultStr = entry.value("default_string", "");
387 if ((defaultStrLen == 0) && (defaultStr.size() > 0))
388 {
389 log<level::ERR>(
390 "Default string length is 0, but default string is existing",
391 phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
392 return -1;
393 }
394
395 // dbus handling
396 std::optional<internal::DBusMapping> dBusMap;
397
398 if (entry.count("dbus") != 0)
399 {
400 auto dBusEntry = entry.value("dbus", emptyJson);
401 dBusMap = std::make_optional<internal::DBusMapping>();
402 dBusMap->objectPath = dBusEntry.value("object_path", "");
403 dBusMap->interface = dBusEntry.value("interface", "");
404 dBusMap->propertyName = dBusEntry.value("property_name", "");
405 if (dBusMap->objectPath.empty() || dBusMap->interface.empty() ||
406 dBusMap->propertyName.empty())
407 {
408 log<level::ERR>(
409 "Invalid dbus config",
410 phosphor::logging::entry("OBJPATH=%s",
411 dBusMap->objectPath.c_str()),
412 phosphor::logging::entry("INTERFACE=%s",
413 dBusMap->interface.c_str()),
414 phosphor::logging::entry("PROPERTY_NAME=%s",
415 dBusMap->propertyName.c_str()));
416 return -1;
417 }
418 }
419
420 internal::attrLookup.emplace(attr, std::move(dBusMap));
421
422 // Defaulting all the types of attributes to BIOSString
423 internal::valueMap.emplace(
424 std::move(attr),
425 std::make_tuple(entry.count("dbus") == 0, strType, minStrLen,
426 maxStrLen, defaultStrLen, std::move(defaultStr)));
427 }
428
429 return rc;
430}
431
432const AttrValuesMap& getValues()
433{
434 return internal::valueMap;
435}
436} // namespace bios_string
437
Tom Joseph52552ef2019-06-20 09:50:15 +0530438Strings getStrings(const char* dirPath)
439{
440 Strings biosStrings{};
441 fs::path dir(dirPath);
442
443 if (!fs::exists(dir) || fs::is_empty(dir))
444 {
445 return biosStrings;
446 }
447
448 for (const auto& file : fs::directory_iterator(dir))
449 {
450 std::ifstream jsonFile(file.path().c_str());
451 if (!jsonFile.is_open())
452 {
453 log<level::ERR>("JSON BIOS config file does not exist",
454 entry("FILE=%s", file.path().filename().c_str()));
455 continue;
456 }
457
458 auto fileData = Json::parse(jsonFile, nullptr, false);
459 if (fileData.is_discarded())
460 {
461 log<level::ERR>("Parsing config file failed",
462 entry("FILE=%s", file.path().filename().c_str()));
463 continue;
464 }
465
Carol Wang612f35b2019-08-26 17:14:26 +0800466 auto entries = fileData.value("entries", emptyJsonList);
Tom Joseph52552ef2019-06-20 09:50:15 +0530467
468 // Iterate through each entry in the config file
469 for (auto& entry : entries)
470 {
471 biosStrings.emplace_back(entry.value("attribute_name", ""));
472
473 // For BIOS enumeration attributes the possible values are strings
474 if (file.path().filename() == bIOSEnumJson)
475 {
476 auto possibleValues = bios_enum::internal::readPossibleValues(
477 entry["possible_values"]);
478 std::move(possibleValues.begin(), possibleValues.end(),
479 std::back_inserter(biosStrings));
480 }
481 }
482 }
483
484 return biosStrings;
485}
486
487} // namespace bios_parser