blob: 30fee04d18b12ae0b1f842a651900282cd8eac73 [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 +080018const std::vector<Json> emptyJsonList{};
19const Json emptyJson{};
20
John Wange96e7e52019-10-05 17:47:30 +080021struct DBusMapping
22{
23 std::string objectPath; //!< D-Bus object path
24 std::string interface; //!< D-Bus interface
25 std::string propertyName; //!< D-Bus property name
26};
Carol Wang612f35b2019-08-26 17:14:26 +080027
John Wange96e7e52019-10-05 17:47:30 +080028using AttrName = std::string;
29using BIOSJsonName = std::string;
30using AttrLookup = std::map<AttrName, std::optional<DBusMapping>>;
31using BIOSStringHandler =
32 std::function<int(const Json& entry, Strings& strings)>;
33using AttrLookupHandler = std::function<int(const Json& entry, AttrLookup)>;
34using typeHandler = std::function<int(const Json& entry)>;
35
36Strings BIOSStrings;
37AttrLookup BIOSAttrLookup;
38
39const Strings& getStrings()
40{
41 return BIOSStrings;
42}
43
44int parseBiosJsonFile(const fs::path& dirPath, const std::string& fileName,
Carol Wang612f35b2019-08-26 17:14:26 +080045 Json& fileData)
46{
47 int rc = 0;
48
John Wange96e7e52019-10-05 17:47:30 +080049 fs::path filePath = dirPath / fileName;
Carol Wang612f35b2019-08-26 17:14:26 +080050
51 std::ifstream jsonFile(filePath);
52 if (!jsonFile.is_open())
53 {
54 log<level::ERR>("BIOS config file does not exist",
55 entry("FILE=%s", filePath.c_str()));
56 rc = -1;
57 }
58 else
59 {
60 fileData = Json::parse(jsonFile, nullptr, false);
61 if (fileData.is_discarded())
62 {
63 log<level::ERR>("Parsing config file failed",
64 entry("FILE=%s", filePath.c_str()));
65 rc = -1;
66 }
67 }
68
69 return rc;
70}
71
Tom Joseph52552ef2019-06-20 09:50:15 +053072namespace bios_enum
73{
74
75namespace internal
76{
77
78using PropertyValue =
79 std::variant<bool, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t,
80 uint64_t, double, std::string>;
81using Value = std::string;
82
John Wange96e7e52019-10-05 17:47:30 +080083/** @brief Map of DBus property value to attribute value
Tom Joseph52552ef2019-06-20 09:50:15 +053084 */
John Wange96e7e52019-10-05 17:47:30 +080085using DbusValToValMap = std::map<PropertyValue, Value>;
86
87/** @brief Map containing the DBus property value to attribute value map for the
88 * BIOS enumeration type attributes
89 */
90std::map<AttrName, DbusValToValMap> dbusValToValMaps;
Tom Joseph52552ef2019-06-20 09:50:15 +053091
92/** @brief Map containing the possible and the default values for the BIOS
93 * enumeration type attributes.
94 */
95AttrValuesMap valueMap;
96
Tom Joseph52552ef2019-06-20 09:50:15 +053097/** @brief Populate the mapping between D-Bus property value and attribute value
98 * for the BIOS enumeration attribute.
99 *
100 * @param[in] type - type of the D-Bus property
101 * @param[in] dBusValues - json array of D-Bus property values
102 * @param[in] pv - Possible values for the BIOS enumeration attribute
Tom Joseph52552ef2019-06-20 09:50:15 +0530103 *
104 */
John Wange96e7e52019-10-05 17:47:30 +0800105DbusValToValMap populateMapping(const std::string& type, const Json& dBusValues,
106 const PossibleValues& pv)
Tom Joseph52552ef2019-06-20 09:50:15 +0530107{
108 size_t pos = 0;
109 PropertyValue value;
John Wange96e7e52019-10-05 17:47:30 +0800110 DbusValToValMap valueMap;
Tom Joseph52552ef2019-06-20 09:50:15 +0530111 for (auto it = dBusValues.begin(); it != dBusValues.end(); ++it, ++pos)
112 {
113 if (type == "uint8_t")
114 {
115 value = static_cast<uint8_t>(it.value());
116 }
117 else if (type == "uint16_t")
118 {
119 value = static_cast<uint16_t>(it.value());
120 }
121 else if (type == "uint32_t")
122 {
123 value = static_cast<uint32_t>(it.value());
124 }
125 else if (type == "uint64_t")
126 {
127 value = static_cast<uint64_t>(it.value());
128 }
129 else if (type == "int16_t")
130 {
131 value = static_cast<int16_t>(it.value());
132 }
133 else if (type == "int32_t")
134 {
135 value = static_cast<int32_t>(it.value());
136 }
137 else if (type == "int64_t")
138 {
139 value = static_cast<int64_t>(it.value());
140 }
141 else if (type == "bool")
142 {
143 value = static_cast<bool>(it.value());
144 }
145 else if (type == "double")
146 {
147 value = static_cast<double>(it.value());
148 }
149 else if (type == "string")
150 {
151 value = static_cast<std::string>(it.value());
152 }
153 else
154 {
155 log<level::ERR>("Unknown D-Bus property type",
156 entry("TYPE=%s", type.c_str()));
157 }
158
John Wange96e7e52019-10-05 17:47:30 +0800159 valueMap.emplace(value, pv[pos]);
Tom Joseph52552ef2019-06-20 09:50:15 +0530160 }
161
John Wange96e7e52019-10-05 17:47:30 +0800162 return valueMap;
Tom Joseph52552ef2019-06-20 09:50:15 +0530163}
164
165} // namespace internal
166
John Wange96e7e52019-10-05 17:47:30 +0800167int setupBIOSStrings(const Json& entry, Strings& strings)
Tom Joseph52552ef2019-06-20 09:50:15 +0530168{
John Wange96e7e52019-10-05 17:47:30 +0800169 Json pvs = entry.value("possible_values", emptyJsonList);
Tom Joseph52552ef2019-06-20 09:50:15 +0530170
John Wange96e7e52019-10-05 17:47:30 +0800171 for (auto& pv : pvs)
Tom Joseph52552ef2019-06-20 09:50:15 +0530172 {
John Wange96e7e52019-10-05 17:47:30 +0800173 strings.emplace_back(std::move(pv.get<std::string>()));
Tom Joseph52552ef2019-06-20 09:50:15 +0530174 }
175
John Wange96e7e52019-10-05 17:47:30 +0800176 return 0;
177}
Tom Joseph52552ef2019-06-20 09:50:15 +0530178
John Wange96e7e52019-10-05 17:47:30 +0800179int setup(const Json& entry)
180{
181 PossibleValues possibleValues;
182 DefaultValues defaultValues;
183
184 std::string attrName = entry.value("attribute_name", "");
185 Json pv = entry["possible_values"];
186 for (auto& val : pv)
Tom Joseph52552ef2019-06-20 09:50:15 +0530187 {
John Wange96e7e52019-10-05 17:47:30 +0800188 possibleValues.emplace_back(std::move(val));
Tom Joseph52552ef2019-06-20 09:50:15 +0530189 }
John Wange96e7e52019-10-05 17:47:30 +0800190 Json dv = entry["default_values"];
191 for (auto& val : dv)
Tom Joseph52552ef2019-06-20 09:50:15 +0530192 {
John Wange96e7e52019-10-05 17:47:30 +0800193 defaultValues.emplace_back(std::move(val));
Tom Joseph52552ef2019-06-20 09:50:15 +0530194 }
John Wange96e7e52019-10-05 17:47:30 +0800195 if (entry.count("dbus") != 0)
Tom Joseph52552ef2019-06-20 09:50:15 +0530196 {
John Wange96e7e52019-10-05 17:47:30 +0800197 auto dbusEntry = entry.value("dbus", emptyJson);
198 std::string propertyType = dbusEntry.value("property_type", "");
199 Json propValues = dbusEntry["property_values"];
200 internal::dbusValToValMaps.emplace(
201 attrName, internal::populateMapping(propertyType, propValues,
202 possibleValues));
Tom Joseph52552ef2019-06-20 09:50:15 +0530203 }
John Wange96e7e52019-10-05 17:47:30 +0800204 // Defaulting all the types of attributes to BIOSEnumeration
205 internal::valueMap.emplace(std::move(attrName),
206 std::make_tuple(entry.count("dbus") == 0,
207 std::move(possibleValues),
208 std::move(defaultValues)));
209 return 0;
Tom Joseph52552ef2019-06-20 09:50:15 +0530210}
211
212const AttrValuesMap& getValues()
213{
214 return internal::valueMap;
215}
216
217CurrentValues getAttrValue(const AttrName& attrName)
218{
John Wange96e7e52019-10-05 17:47:30 +0800219 const auto& dBusMap = BIOSAttrLookup.at(attrName);
Tom Joseph52552ef2019-06-20 09:50:15 +0530220 CurrentValues currentValues;
221 internal::PropertyValue propValue;
222
223 if (dBusMap == std::nullopt)
224 {
225 const auto& valueEntry = internal::valueMap.at(attrName);
226 const auto& [readOnly, possibleValues, currentValues] = valueEntry;
227 return currentValues;
228 }
229
John Wange96e7e52019-10-05 17:47:30 +0800230 const auto& dbusValToValMap = internal::dbusValToValMaps.at(attrName);
Tom Joseph52552ef2019-06-20 09:50:15 +0530231 auto bus = sdbusplus::bus::new_default();
232 auto service = pldm::responder::getService(bus, dBusMap.value().objectPath,
233 dBusMap.value().interface);
234 auto method =
235 bus.new_method_call(service.c_str(), dBusMap.value().objectPath.c_str(),
236 "org.freedesktop.DBus.Properties", "Get");
John Wange96e7e52019-10-05 17:47:30 +0800237 method.append(dBusMap->interface, dBusMap->propertyName);
Tom Joseph52552ef2019-06-20 09:50:15 +0530238 auto reply = bus.call(method);
239 reply.read(propValue);
240
John Wange96e7e52019-10-05 17:47:30 +0800241 auto iter = dbusValToValMap.find(propValue);
242 if (iter != dbusValToValMap.end())
Tom Joseph52552ef2019-06-20 09:50:15 +0530243 {
244 currentValues.push_back(iter->second);
245 }
246
247 return currentValues;
248}
249
250} // namespace bios_enum
251
Carol Wang612f35b2019-08-26 17:14:26 +0800252namespace bios_string
253{
254
255/** @brief BIOS string types
256 */
257enum BiosStringEncoding
258{
259 UNKNOWN = 0x00,
260 ASCII = 0x01,
261 HEX = 0x02,
262 UTF_8 = 0x03,
263 UTF_16LE = 0x04,
264 UTF_16BE = 0x05,
265 VENDOR_SPECIFIC = 0xFF
266};
267
268const std::map<std::string, uint8_t> strTypeMap{
269 {"Unknown", UNKNOWN},
270 {"ASCII", ASCII},
271 {"Hex", HEX},
272 {"UTF-8", UTF_8},
273 {"UTF-16LE", UTF_16LE},
274 {"UTF-16LE", UTF_16LE},
275 {"Vendor Specific", VENDOR_SPECIFIC}};
276
277namespace internal
278{
279
Carol Wang612f35b2019-08-26 17:14:26 +0800280/** @brief Map containing the possible and the default values for the BIOS
281 * string type attributes.
282 */
283AttrValuesMap valueMap;
284
Carol Wang612f35b2019-08-26 17:14:26 +0800285} // namespace internal
286
John Wange96e7e52019-10-05 17:47:30 +0800287int setup(const Json& entry)
Carol Wang612f35b2019-08-26 17:14:26 +0800288{
Carol Wang612f35b2019-08-26 17:14:26 +0800289
John Wange96e7e52019-10-05 17:47:30 +0800290 std::string attr = entry.value("attribute_name", "");
291 // Transfer string type from string to enum
292 std::string strTypeTmp = entry.value("string_type", "Unknown");
293 auto iter = strTypeMap.find(strTypeTmp);
294 if (iter == strTypeMap.end())
Carol Wang612f35b2019-08-26 17:14:26 +0800295 {
John Wange96e7e52019-10-05 17:47:30 +0800296 log<level::ERR>(
297 "Wrong string type",
298 phosphor::logging::entry("STRING_TYPE=%s", strTypeTmp.c_str()),
299 phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
300 return -1;
301 }
302 uint8_t strType = iter->second;
303
304 uint16_t minStrLen = entry.value("minimum_string_length", 0);
305 uint16_t maxStrLen = entry.value("maximum_string_length", 0);
306 if (maxStrLen - minStrLen < 0)
307 {
308 log<level::ERR>(
309 "Maximum string length is smaller than minimum string length",
310 phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
311 return -1;
312 }
313 uint16_t defaultStrLen = entry.value("default_string_length", 0);
314 std::string defaultStr = entry.value("default_string", "");
315 if ((defaultStrLen == 0) && (defaultStr.size() > 0))
316 {
317 log<level::ERR>(
318 "Default string length is 0, but default string is existing",
319 phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
320 return -1;
Carol Wang612f35b2019-08-26 17:14:26 +0800321 }
322
John Wange96e7e52019-10-05 17:47:30 +0800323 // Defaulting all the types of attributes to BIOSString
324 internal::valueMap.emplace(
325 std::move(attr),
326 std::make_tuple(entry.count("dbus") == 0, strType, minStrLen, maxStrLen,
327 defaultStrLen, std::move(defaultStr)));
Carol Wang612f35b2019-08-26 17:14:26 +0800328
John Wange96e7e52019-10-05 17:47:30 +0800329 return 0;
Carol Wang612f35b2019-08-26 17:14:26 +0800330}
331
332const AttrValuesMap& getValues()
333{
334 return internal::valueMap;
335}
Carol Wangb503f9e2019-09-02 16:34:10 +0800336
337std::string getAttrValue(const AttrName& attrName)
338{
John Wange96e7e52019-10-05 17:47:30 +0800339 const auto& dBusMap = BIOSAttrLookup.at(attrName);
Carol Wangb503f9e2019-09-02 16:34:10 +0800340 std::variant<std::string> propValue;
341
342 if (dBusMap == std::nullopt)
343 { // return default string
344 const auto& valueEntry = internal::valueMap.at(attrName);
345 return std::get<DefaultStr>(valueEntry);
346 }
347
348 auto bus = sdbusplus::bus::new_default();
349 auto service = pldm::responder::getService(bus, dBusMap->objectPath,
350 dBusMap->interface);
351 auto method =
352 bus.new_method_call(service.c_str(), dBusMap->objectPath.c_str(),
353 "org.freedesktop.DBus.Properties", "Get");
354 method.append(dBusMap->interface, dBusMap->propertyName);
355 auto reply = bus.call(method);
356 reply.read(propValue);
357
358 return std::get<std::string>(propValue);
359}
360
Carol Wang612f35b2019-08-26 17:14:26 +0800361} // namespace bios_string
362
John Wange96e7e52019-10-05 17:47:30 +0800363const std::map<BIOSJsonName, BIOSStringHandler> BIOSStringHandlers = {
364 {bIOSEnumJson, bios_enum::setupBIOSStrings},
365};
Tom Joseph52552ef2019-06-20 09:50:15 +0530366
John Wange96e7e52019-10-05 17:47:30 +0800367const std::map<BIOSJsonName, typeHandler> BIOSTypeHandlers = {
368 {bIOSEnumJson, bios_enum::setup},
369 {bIOSStrJson, bios_string::setup},
370};
371
372void setupBIOSStrings(const BIOSJsonName& jsonName, const Json& entry,
373 Strings& strings)
374{
375 strings.emplace_back(entry.value("attribute_name", ""));
376 auto iter = BIOSStringHandlers.find(jsonName);
377 if (iter != BIOSStringHandlers.end())
378 {
379 iter->second(entry, strings);
380 }
381}
382
383void setupBIOSAttrLookup(const Json& entry, AttrLookup& lookup)
384{
385 std::optional<DBusMapping> dBusMap;
386 std::string attrName = entry.value("attribute_name", "");
387
388 if (entry.count("dbus") != 0)
389 {
390 auto dBusEntry = entry.value("dbus", emptyJson);
391 std::string objectPath = dBusEntry.value("object_path", "");
392 std::string interface = dBusEntry.value("interface", "");
393 std::string propertyName = dBusEntry.value("property_name", "");
394 if (!objectPath.empty() && !interface.empty() && !propertyName.empty())
395 {
396 dBusMap = std::optional<DBusMapping>(
397 {objectPath, interface, propertyName});
398 }
399 else
400 {
401 log<level::ERR>(
402 "Invalid dbus config",
403 phosphor::logging::entry("OBJPATH=%s",
404 dBusMap->objectPath.c_str()),
405 phosphor::logging::entry("INTERFACE=%s",
406 dBusMap->interface.c_str()),
407 phosphor::logging::entry("PROPERTY_NAME=%s",
408 dBusMap->propertyName.c_str()));
409 }
410 }
411 lookup.emplace(attrName, dBusMap);
412}
413
414int setupBIOSType(const BIOSJsonName& jsonName, const Json& entry)
415{
416 auto iter = BIOSTypeHandlers.find(jsonName);
417 if (iter != BIOSTypeHandlers.end())
418 {
419 iter->second(entry);
420 }
421 return 0;
422}
423
424const std::vector<BIOSJsonName> BIOSConfigFiles = {bIOSEnumJson, bIOSStrJson};
425
426int setupConfig(const char* dirPath)
427{
428 if (!BIOSStrings.empty() && !BIOSAttrLookup.empty())
429 {
430 return 0;
431 }
432
433 fs::path dir(dirPath);
Tom Joseph52552ef2019-06-20 09:50:15 +0530434 if (!fs::exists(dir) || fs::is_empty(dir))
435 {
John Wange96e7e52019-10-05 17:47:30 +0800436 log<level::ERR>("BIOS config directory does not exist or empty",
437 entry("DIR=%s", dirPath));
438 return -1;
Tom Joseph52552ef2019-06-20 09:50:15 +0530439 }
John Wange96e7e52019-10-05 17:47:30 +0800440 for (auto jsonName : BIOSConfigFiles)
Tom Joseph52552ef2019-06-20 09:50:15 +0530441 {
John Wange96e7e52019-10-05 17:47:30 +0800442 Json json;
443 if (parseBiosJsonFile(dir, jsonName, json) < 0)
Tom Joseph52552ef2019-06-20 09:50:15 +0530444 {
Tom Joseph52552ef2019-06-20 09:50:15 +0530445 continue;
446 }
John Wange96e7e52019-10-05 17:47:30 +0800447 auto entries = json.value("entries", emptyJsonList);
Tom Joseph52552ef2019-06-20 09:50:15 +0530448 for (auto& entry : entries)
449 {
John Wange96e7e52019-10-05 17:47:30 +0800450 setupBIOSStrings(jsonName, entry, BIOSStrings);
451 setupBIOSAttrLookup(entry, BIOSAttrLookup);
452 setupBIOSType(jsonName, entry);
Tom Joseph52552ef2019-06-20 09:50:15 +0530453 }
454 }
John Wange96e7e52019-10-05 17:47:30 +0800455 if (BIOSStrings.empty())
456 { // means there is no attribute
457 log<level::ERR>("No attribute is found in the config directory",
458 entry("DIR=%s", dirPath));
459 return -1;
460 }
461 return 0;
Tom Joseph52552ef2019-06-20 09:50:15 +0530462}
463
464} // namespace bios_parser